/* BFD back-end for VERSAdos-E objects. Copyright (C) 1995-2014 Free Software Foundation, Inc. Written by Steve Chamberlain of Cygnus Support . Versados is a Motorola trademark. This file is part of BFD, the Binary File Descriptor library. This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /* SUBSECTION VERSAdos-E relocatable object file format DESCRIPTION This module supports reading of VERSAdos relocatable object files. A VERSAdos file looks like contains o Identification Record o External Symbol Definition Record o Object Text Record o End Record. */ #include "sysdep.h" #include "bfd.h" #include "libbfd.h" #include "libiberty.h" #define VHEADER '1' #define VESTDEF '2' #define VOTR '3' #define VEND '4' #define ES_BASE 17 /* First symbol has esdid 17. */ /* Per file target dependent information. */ /* One for each section. */ struct esdid { asection *section; /* Ptr to bfd version. */ unsigned char *contents; /* Used to build image. */ bfd_size_type content_size; /* The size of the contents buffer. */ int pc; int relocs; /* Reloc count, valid end of pass 1. */ int donerel; /* Have relocs been translated. */ }; typedef struct versados_data_struct { int es_done; /* Count of symbol index, starts at ES_BASE. */ asymbol *symbols; /* Pointer to local symbols. */ char *strings; /* Strings of all the above. */ int stringlen; /* Len of string table (valid end of pass1). */ int nsecsyms; /* Number of sections. */ int ndefs; /* Number of exported symbols (they dont get esdids). */ int nrefs; /* Number of imported symbols (valid end of pass1). */ int ref_idx; /* Current processed value of the above. */ int def_idx; int pass_2_done; struct esdid e[16]; /* Per section info. */ int alert; /* To see if we're trampling. */ asymbol *rest[256 - 16]; /* Per symbol info. */ } tdata_type; #define VDATA(abfd) (abfd->tdata.versados_data) #define EDATA(abfd, n) (abfd->tdata.versados_data->e[(n) < 16 ? (n) : 0]) #define RDATA(abfd, n) (abfd->tdata.versados_data->rest[(n) < 240 ? (n) : 0]) struct ext_otr { unsigned char size; char type; unsigned char map[4]; unsigned char esdid; unsigned char data[200]; }; struct ext_vheader { unsigned char size; char type; /* Record type. */ char name[10]; /* Module name. */ char rev; /* Module rev number. */ char lang; char vol[4]; char user[2]; char cat[8]; char fname[8]; char ext[2]; char time[3]; char date[3]; char rest[211]; }; struct ext_esd { unsigned char size; char type; unsigned char esd_entries[1]; }; #define ESD_ABS 0 #define ESD_COMMON 1 #define ESD_STD_REL_SEC 2 #define ESD_SHRT_REL_SEC 3 #define ESD_XDEF_IN_SEC 4 #define ESD_XDEF_IN_ABS 5 #define ESD_XREF_SEC 6 #define ESD_XREF_SYM 7 union ext_any { unsigned char size; struct ext_vheader header; struct ext_esd esd; struct ext_otr otr; }; /* Initialize by filling in the hex conversion array. */ /* Set up the tdata information. */ static bfd_boolean versados_mkobject (bfd *abfd) { if (abfd->tdata.versados_data == NULL) { bfd_size_type amt = sizeof (tdata_type); tdata_type *tdata = bfd_alloc (abfd, amt); if (tdata == NULL) return FALSE; abfd->tdata.versados_data = tdata; tdata->symbols = NULL; VDATA (abfd)->alert = 0x12345678; } bfd_default_set_arch_mach (abfd, bfd_arch_m68k, 0); return TRUE; } /* Report a problem in an S record file. FIXME: This probably should not call fprintf, but we really do need some mechanism for printing error messages. */ static asymbol * versados_new_symbol (bfd *abfd, int snum, const char *name, bfd_vma val, asection *sec) { asymbol *n = VDATA (abfd)->symbols + snum; n->name = name; n->value = val; n->section = sec; n->the_bfd = abfd; n->flags = 0; return n; } static bfd_boolean get_record (bfd *abfd, union ext_any *ptr) { if (bfd_bread (&ptr->size, (bfd_size_type) 1, abfd) != 1 || (bfd_bread ((char *) ptr + 1, (bfd_size_type) ptr->size, abfd) != ptr->size)) return FALSE; { bfd_size_type amt = ptr->size + 1; if (amt < sizeof (* ptr)) memset ((char *) ptr + amt, 0, sizeof (* ptr) - amt); } return TRUE; } static int get_4 (unsigned char **pp) { unsigned char *p = *pp; *pp += 4; return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0); } static void get_10 (unsigned char **pp, char *name) { char *p = (char *) *pp; int len = 10; *pp += len; while (*p != ' ' && len) { *name++ = *p++; len--; } *name = 0; } static char * new_symbol_string (bfd *abfd, const char *name) { char *n = VDATA (abfd)->strings; strcpy (VDATA (abfd)->strings, name); VDATA (abfd)->strings += strlen (VDATA (abfd)->strings) + 1; return n; } static void process_esd (bfd *abfd, struct ext_esd *esd, int pass) { /* Read through the ext def for the est entries. */ int togo = esd->size - 2; bfd_vma size; bfd_vma start; asection *sec; char name[11]; unsigned char *ptr = esd->esd_entries; unsigned char *end = ptr + togo; while (ptr < end) { int scn = *ptr & 0xf; int typ = (*ptr >> 4) & 0xf; /* Declare this section. */ sprintf (name, "%d", scn); sec = bfd_make_section_old_way (abfd, strdup (name)); sec->target_index = scn; EDATA (abfd, scn).section = sec; ptr++; switch (typ) { default: abort (); case ESD_XREF_SEC: case ESD_XREF_SYM: { int snum = VDATA (abfd)->ref_idx++; get_10 (&ptr, name); if (pass == 1) VDATA (abfd)->stringlen += strlen (name) + 1; else { int esidx; asymbol *s; char *n = new_symbol_string (abfd, name); s = versados_new_symbol (abfd, snum, n, (bfd_vma) 0, bfd_und_section_ptr); esidx = VDATA (abfd)->es_done++; RDATA (abfd, esidx - ES_BASE) = s; } } break; case ESD_ABS: size = get_4 (&ptr); (void) size; start = get_4 (&ptr); (void) start; break; case ESD_STD_REL_SEC: case ESD_SHRT_REL_SEC: sec->size = get_4 (&ptr); sec->flags |= SEC_ALLOC; break; case ESD_XDEF_IN_ABS: sec = bfd_abs_section_ptr; case ESD_XDEF_IN_SEC: { int snum = VDATA (abfd)->def_idx++; bfd_vma val; get_10 (&ptr, name); val = get_4 (&ptr); if (pass == 1) /* Just remember the symbol. */ VDATA (abfd)->stringlen += strlen (name) + 1; else { asymbol *s; char *n = new_symbol_string (abfd, name); s = versados_new_symbol (abfd, snum + VDATA (abfd)->nrefs, n, val, sec); s->flags |= BSF_GLOBAL; } } break; } } } #define R_RELWORD 1 #define R_RELLONG 2 #define R_RELWORD_NEG 3 #define R_RELLONG_NEG 4 reloc_howto_type versados_howto_table[] = { HOWTO (R_RELWORD, 0, 1, 16, FALSE, 0, complain_overflow_dont, 0, "+v16", TRUE, 0x0000ffff, 0x0000ffff, FALSE), HOWTO (R_RELLONG, 0, 2, 32, FALSE, 0, complain_overflow_dont, 0, "+v32", TRUE, 0xffffffff, 0xffffffff, FALSE), HOWTO (R_RELWORD_NEG, 0, -1, 16, FALSE, 0, complain_overflow_dont, 0, "-v16", TRUE, 0x0000ffff, 0x0000ffff, FALSE), HOWTO (R_RELLONG_NEG, 0, -2, 32, FALSE, 0, complain_overflow_dont, 0, "-v32", TRUE, 0xffffffff, 0xffffffff, FALSE), }; static int get_offset (int len, unsigned char *ptr) { int val = 0; if (len) { int i; val = *ptr++; if (val & 0x80) val |= ~0xff; for (i = 1; i < len; i++) val = (val << 8) | *ptr++; } return val; } static void process_otr (bfd *abfd, struct ext_otr *otr, int pass) { unsigned long shift; unsigned char *srcp = otr->data; unsigned char *endp = (unsigned char *) otr + otr->size; unsigned int bits = (otr->map[0] << 24) | (otr->map[1] << 16) | (otr->map[2] << 8) | (otr->map[3] << 0); struct esdid *esdid; unsigned char *contents; bfd_boolean need_contents = FALSE; unsigned int dst_idx; /* PR 17512: file: ac7da425. */ if (otr->esdid == 0) return; esdid = &EDATA (abfd, otr->esdid - 1); contents = esdid->contents; dst_idx = esdid->pc; for (shift = ((unsigned long) 1 << 31); shift && srcp < endp; shift >>= 1) { if (bits & shift) { int flag = *srcp++; int esdids = (flag >> 5) & 0x7; int sizeinwords = ((flag >> 3) & 1) ? 2 : 1; int offsetlen = flag & 0x7; int j; if (esdids == 0) { /* A zero esdid means the new pc is the offset given. */ dst_idx += get_offset (offsetlen, srcp); srcp += offsetlen; } else { int val = get_offset (offsetlen, srcp + esdids); if (pass == 1) need_contents = TRUE; else if (contents && dst_idx < esdid->content_size - sizeinwords * 2) for (j = 0; j < sizeinwords * 2; j++) { contents[dst_idx + (sizeinwords * 2) - j - 1] = val; val >>= 8; } for (j = 0; j < esdids; j++) { int id = *srcp++; if (id) { int rn = EDATA (abfd, otr->esdid - 1).relocs++; if (pass == 1) { /* This is the first pass over the data, just remember that we need a reloc. */ } else { arelent *n; /* PR 17512: file: 54f733e0. */ if (EDATA (abfd, otr->esdid - 1).section == NULL) continue; n = EDATA (abfd, otr->esdid - 1).section->relocation + rn; n->address = dst_idx; n->sym_ptr_ptr = (asymbol **) (size_t) id; n->addend = 0; n->howto = versados_howto_table + ((j & 1) * 2) + (sizeinwords - 1); } } } srcp += offsetlen; dst_idx += sizeinwords * 2; } } else { need_contents = TRUE; if (esdid->section && contents && dst_idx < esdid->content_size - 1) if (pass == 2) { /* Absolute code, comes in 16 bit lumps. */ contents[dst_idx] = srcp[0]; contents[dst_idx + 1] = srcp[1]; } dst_idx += 2; srcp += 2; } } EDATA (abfd, otr->esdid - 1).pc = dst_idx; if (!contents && need_contents) { if (esdid->section) { bfd_size_type size; size = esdid->section->size; esdid->contents = bfd_alloc (abfd, size); esdid->content_size = size; } else esdid->contents = NULL; } } static bfd_boolean versados_scan (bfd *abfd) { bfd_boolean loop = TRUE; int i; int j; int nsecs = 0; bfd_size_type amt; VDATA (abfd)->stringlen = 0; VDATA (abfd)->nrefs = 0; VDATA (abfd)->ndefs = 0; VDATA (abfd)->ref_idx = 0; VDATA (abfd)->def_idx = 0; VDATA (abfd)->pass_2_done = 0; while (loop) { union ext_any any; if (!get_record (abfd, &any)) return FALSE; switch (any.header.type) { case VHEADER: break; case VEND: loop = FALSE; break; case VESTDEF: process_esd (abfd, &any.esd, 1); break; case VOTR: process_otr (abfd, &any.otr, 1); break; } } /* Now allocate space for the relocs and sections. */ VDATA (abfd)->nrefs = VDATA (abfd)->ref_idx; VDATA (abfd)->ndefs = VDATA (abfd)->def_idx; VDATA (abfd)->ref_idx = 0; VDATA (abfd)->def_idx = 0; abfd->symcount = VDATA (abfd)->nrefs + VDATA (abfd)->ndefs; for (i = 0; i < 16; i++) { struct esdid *esdid = &EDATA (abfd, i); if (esdid->section) { amt = (bfd_size_type) esdid->relocs * sizeof (arelent); esdid->section->relocation = bfd_alloc (abfd, amt); esdid->pc = 0; if (esdid->contents) esdid->section->flags |= SEC_HAS_CONTENTS | SEC_LOAD; esdid->section->reloc_count = esdid->relocs; if (esdid->relocs) esdid->section->flags |= SEC_RELOC; esdid->relocs = 0; /* Add an entry into the symbol table for it. */ nsecs++; VDATA (abfd)->stringlen += strlen (esdid->section->name) + 1; } } abfd->symcount += nsecs; amt = abfd->symcount; amt *= sizeof (asymbol); VDATA (abfd)->symbols = bfd_alloc (abfd, amt); amt = VDATA (abfd)->stringlen; VDATA (abfd)->strings = bfd_alloc (abfd, amt); if ((VDATA (abfd)->symbols == NULL && abfd->symcount > 0) || (VDATA (abfd)->strings == NULL && VDATA (abfd)->stringlen > 0)) return FALSE; /* Actually fill in the section symbols, we stick them at the end of the table. */ for (j = VDATA (abfd)->nrefs + VDATA (abfd)->ndefs, i = 0; i < 16; i++) { struct esdid *esdid = &EDATA (abfd, i); asection *sec = esdid->section; if (sec) { asymbol *s = VDATA (abfd)->symbols + j; s->name = new_symbol_string (abfd, sec->name); s->section = sec; s->flags = BSF_LOCAL; s->value = 0; s->the_bfd = abfd; j++; } } if (abfd->symcount) abfd->flags |= HAS_SYMS; /* Set this to nsecs - since we've already planted the section symbols. */ VDATA (abfd)->nsecsyms = nsecs; VDATA (abfd)->ref_idx = 0; return TRUE; } /* Check whether an existing file is a versados file. */ static const bfd_target * versados_object_p (bfd *abfd) { struct ext_vheader ext; unsigned char len; tdata_type *tdata_save; if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0) return NULL; if (bfd_bread (&len, (bfd_size_type) 1, abfd) != 1) { if (bfd_get_error () != bfd_error_system_call) bfd_set_error (bfd_error_wrong_format); return NULL; } /* PR 17512: file: 726-2128-0.004. */ if (len < 13) { bfd_set_error (bfd_error_wrong_format); return NULL; } if (bfd_bread (&ext.type, (bfd_size_type) len, abfd) != len) { if (bfd_get_error () != bfd_error_system_call) bfd_set_error (bfd_error_wrong_format); return NULL; } /* We guess that the language field will never be larger than 10. In sample files, it is always either 0 or 1. Checking for this prevents confusion with Intel Hex files. */ if (ext.type != VHEADER || ext.lang > 10) { bfd_set_error (bfd_error_wrong_format); return NULL; } /* OK, looks like a record, build the tdata and read in. */ tdata_save = abfd->tdata.versados_data; if (!versados_mkobject (abfd) || !versados_scan (abfd)) { abfd->tdata.versados_data = tdata_save; return NULL; } return abfd->xvec; } static bfd_boolean versados_pass_2 (bfd *abfd) { union ext_any any; if (VDATA (abfd)->pass_2_done) return 1; if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0) return 0; VDATA (abfd)->es_done = ES_BASE; /* Read records till we get to where we want to be. */ while (1) { get_record (abfd, &any); switch (any.header.type) { case VEND: VDATA (abfd)->pass_2_done = 1; return 1; case VESTDEF: process_esd (abfd, &any.esd, 2); break; case VOTR: process_otr (abfd, &any.otr, 2); break; } } } static bfd_boolean versados_get_section_contents (bfd *abfd, asection *section, void * location, file_ptr offset, bfd_size_type count) { struct esdid *esdid; if (!versados_pass_2 (abfd)) return FALSE; esdid = &EDATA (abfd, section->target_index); if (esdid->contents == NULL || offset < 0 || (bfd_size_type) offset > esdid->content_size || offset + count > esdid->content_size) return FALSE; memcpy (location, esdid->contents + offset, (size_t) count); return TRUE; } #define versados_get_section_contents_in_window \ _bfd_generic_get_section_contents_in_window static bfd_boolean versados_set_section_contents (bfd *abfd ATTRIBUTE_UNUSED, sec_ptr section ATTRIBUTE_UNUSED, const void * location ATTRIBUTE_UNUSED, file_ptr offset ATTRIBUTE_UNUSED, bfd_size_type bytes_to_do ATTRIBUTE_UNUSED) { return FALSE; } static int versados_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED, struct bfd_link_info *info ATTRIBUTE_UNUSED) { return 0; } /* Return the amount of memory needed to read the symbol table. */ static long versados_get_symtab_upper_bound (bfd *abfd) { return (bfd_get_symcount (abfd) + 1) * sizeof (asymbol *); } /* Return the symbol table. */ static long versados_canonicalize_symtab (bfd *abfd, asymbol **alocation) { unsigned int symcount = bfd_get_symcount (abfd); unsigned int i; asymbol *s; versados_pass_2 (abfd); for (i = 0, s = VDATA (abfd)->symbols; i < symcount; s++, i++) *alocation++ = s; *alocation = NULL; return symcount; } static void versados_get_symbol_info (bfd *abfd ATTRIBUTE_UNUSED, asymbol *symbol, symbol_info *ret) { bfd_symbol_info (symbol, ret); } static void versados_print_symbol (bfd *abfd, void * afile, asymbol *symbol, bfd_print_symbol_type how) { FILE *file = (FILE *) afile; switch (how) { case bfd_print_symbol_name: fprintf (file, "%s", symbol->name); break; default: bfd_print_symbol_vandf (abfd, (void *) file, symbol); fprintf (file, " %-5s %s", symbol->section->name, symbol->name); } } static long versados_get_reloc_upper_bound (bfd *abfd ATTRIBUTE_UNUSED, sec_ptr asect) { return (asect->reloc_count + 1) * sizeof (arelent *); } static long versados_canonicalize_reloc (bfd *abfd, sec_ptr section, arelent **relptr, asymbol **symbols) { unsigned int count; arelent *src; versados_pass_2 (abfd); src = section->relocation; if (!EDATA (abfd, section->target_index).donerel) { EDATA (abfd, section->target_index).donerel = 1; /* Translate from indexes to symptr ptrs. */ for (count = 0; count < section->reloc_count; count++) { int esdid = (int) (size_t) src[count].sym_ptr_ptr; if (esdid == 0) src[count].sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; else if (esdid < ES_BASE) { /* Section relative thing. */ struct esdid *e = &EDATA (abfd, esdid - 1); /* PR 17512: file:cd92277c. */ if (e->section) src[count].sym_ptr_ptr = e->section->symbol_ptr_ptr; else src[count].sym_ptr_ptr = bfd_und_section_ptr->symbol_ptr_ptr; } /* PR 17512: file:3757-2936-0.004. */ else if ((unsigned) (esdid - ES_BASE) >= bfd_get_symcount (abfd)) src[count].sym_ptr_ptr = bfd_und_section_ptr->symbol_ptr_ptr; else src[count].sym_ptr_ptr = symbols + esdid - ES_BASE; } } for (count = 0; count < section->reloc_count; count++) *relptr++ = src++; *relptr = 0; return section->reloc_count; } #define versados_close_and_cleanup _bfd_generic_close_and_cleanup #define versados_bfd_free_cached_info _bfd_generic_bfd_free_cached_info #define versados_new_section_hook _bfd_generic_new_section_hook #define versados_bfd_is_target_special_symbol ((bfd_boolean (*) (bfd *, asymbol *)) bfd_false) #define versados_bfd_is_local_label_name bfd_generic_is_local_label_name #define versados_get_lineno _bfd_nosymbols_get_lineno #define versados_find_nearest_line _bfd_nosymbols_find_nearest_line #define versados_find_line _bfd_nosymbols_find_line #define versados_find_inliner_info _bfd_nosymbols_find_inliner_info #define versados_get_symbol_version_string _bfd_nosymbols_get_symbol_version_string #define versados_make_empty_symbol _bfd_generic_make_empty_symbol #define versados_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol #define versados_read_minisymbols _bfd_generic_read_minisymbols #define versados_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol #define versados_bfd_reloc_type_lookup _bfd_norelocs_bfd_reloc_type_lookup #define versados_bfd_reloc_name_lookup _bfd_norelocs_bfd_reloc_name_lookup #define versados_set_arch_mach bfd_default_set_arch_mach #define versados_bfd_get_relocated_section_contents bfd_generic_get_relocated_section_contents #define versados_bfd_relax_section bfd_generic_relax_section #define versados_bfd_gc_sections bfd_generic_gc_sections #define versados_bfd_lookup_section_flags bfd_generic_lookup_section_flags #define versados_bfd_merge_sections bfd_generic_merge_sections #define versados_bfd_is_group_section bfd_generic_is_group_section #define versados_bfd_discard_group bfd_generic_discard_group #define versados_section_already_linked _bfd_generic_section_already_linked #define versados_bfd_define_common_symbol bfd_generic_define_common_symbol #define versados_bfd_link_hash_table_create _bfd_generic_link_hash_table_create #define versados_bfd_link_add_symbols _bfd_generic_link_add_symbols #define versados_bfd_link_just_syms _bfd_generic_link_just_syms #define versados_bfd_copy_link_hash_symbol_type \ _bfd_generic_copy_link_hash_symbol_type #define versados_bfd_final_link _bfd_generic_final_link #define versados_bfd_link_split_section _bfd_generic_link_split_section const bfd_target m68k_versados_vec = { "versados", /* Name. */ bfd_target_versados_flavour, BFD_ENDIAN_BIG, /* Target byte order. */ BFD_ENDIAN_BIG, /* Target headers byte order. */ (HAS_RELOC | EXEC_P | /* Object flags. */ HAS_LINENO | HAS_DEBUG | HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED), (SEC_CODE | SEC_DATA | SEC_ROM | SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* Section flags. */ 0, /* Leading underscore. */ ' ', /* AR_pad_char. */ 16, /* AR_max_namelen. */ 0, /* match priority. */ bfd_getb64, bfd_getb_signed_64, bfd_putb64, bfd_getb32, bfd_getb_signed_32, bfd_putb32, bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* Data. */ bfd_getb64, bfd_getb_signed_64, bfd_putb64, bfd_getb32, bfd_getb_signed_32, bfd_putb32, bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* Headers. */ { _bfd_dummy_target, versados_object_p, /* bfd_check_format. */ _bfd_dummy_target, _bfd_dummy_target, }, { bfd_false, versados_mkobject, _bfd_generic_mkarchive, bfd_false, }, { /* bfd_write_contents. */ bfd_false, bfd_false, _bfd_write_archive_contents, bfd_false, }, BFD_JUMP_TABLE_GENERIC (versados), BFD_JUMP_TABLE_COPY (_bfd_generic), BFD_JUMP_TABLE_CORE (_bfd_nocore), BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive), BFD_JUMP_TABLE_SYMBOLS (versados), BFD_JUMP_TABLE_RELOCS (versados), BFD_JUMP_TABLE_WRITE (versados), BFD_JUMP_TABLE_LINK (versados), BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), NULL, NULL };