/* obj-format for ieee-695 records. Copyright (C) 1991, 1992 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. GAS 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 2, or (at your option) any later version. GAS 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 GAS; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* created by steve chamberlain steve@cygnus.com */ /* this will hopefully become the port through which bfd and gas talk, for the moment, only ieee is known to work well. */ #include "bfd.h" #include "as.h" #include "subsegs.h" #include "output-file.h" #include "frags.h" bfd *abfd; /* How many addresses does the .align take? */ static relax_addressT relax_align (address, alignment) register relax_addressT address; /* Address now. */ register long alignment; /* Alignment (binary). */ { relax_addressT mask; relax_addressT new_address; mask = ~((~0) << alignment); new_address = (address + mask) & (~mask); return (new_address - address); } /* relax_align() */ /* calculate the size of the frag chain and create a bfd section to contain all of it */ static void DEFUN (size_section, (abfd, idx), bfd * abfd AND unsigned int idx) { asection *sec; unsigned int size = 0; fragS *frag = segment_info[idx].frag_root; while (frag) { if (frag->fr_address != size) { printf ("Out of step\n"); size = frag->fr_address; } size += frag->fr_fix; switch (frag->fr_type) { case rs_fill: case rs_org: size += frag->fr_offset * frag->fr_var; break; case rs_align: size += relax_align (size, frag->fr_offset); } frag = frag->fr_next; } if (size) { char *name = segment_info[idx].name; if (name == (char *) NULL) { name = ".data"; } segment_info[idx].user_stuff = (char *) (sec = bfd_make_section (abfd, name)); /* Make it output through itself */ sec->output_section = sec; sec->flags |= SEC_HAS_CONTENTS; bfd_set_section_size (abfd, sec, size); } } /* run through a frag chain and write out the data to go with it */ static void DEFUN (fill_section, (abfd, idx), bfd * abfd AND unsigned int idx) { asection *sec = segment_info[idx].user_stuff; if (sec) { fragS *frag = segment_info[idx].frag_root; unsigned int offset = 0; while (frag) { unsigned int fill_size; unsigned int count; switch (frag->fr_type) { case rs_fill: case rs_align: case rs_org: if (frag->fr_fix) { bfd_set_section_contents (abfd, sec, frag->fr_literal, frag->fr_address, frag->fr_fix); } offset += frag->fr_fix; fill_size = frag->fr_var; if (fill_size) { unsigned int off = frag->fr_fix; for (count = frag->fr_offset; count; count--) { bfd_set_section_contents (abfd, sec, frag->fr_literal + frag->fr_fix, frag->fr_address + off, fill_size); off += fill_size; } } break; default: abort (); } frag = frag->fr_next; } } } /* Count the relocations in a chain */ static unsigned int DEFUN (count_entries_in_chain, (idx), unsigned int idx) { unsigned int nrelocs; fixS *fixup_ptr; /* Count the relocations */ fixup_ptr = segment_info[idx].fix_root; nrelocs = 0; while (fixup_ptr != (fixS *) NULL) { fixup_ptr = fixup_ptr->fx_next; nrelocs++; } return nrelocs; } /* output all the relocations for a section */ void DEFUN (do_relocs_for, (idx), unsigned int idx) { unsigned int nrelocs; arelent **reloc_ptr_vector; arelent *reloc_vector; asymbol **ptrs; asection *section = (asection *) (segment_info[idx].user_stuff); unsigned int i; fixS *from; if (section) { nrelocs = count_entries_in_chain (idx); reloc_ptr_vector = (arelent **) malloc ((nrelocs + 1) * sizeof (arelent *)); reloc_vector = (arelent *) malloc (nrelocs * sizeof (arelent)); ptrs = (asymbol **) malloc (nrelocs * sizeof (asymbol *)); from = segment_info[idx].fix_root; for (i = 0; i < nrelocs; i++) { arelent *to = reloc_vector + i; asymbol *s; reloc_ptr_vector[i] = to; to->howto = (reloc_howto_type *) (from->fx_r_type); #if 0 /* We can't represent complicated things in a reloc yet */ if (from->fx_addsy == 0 || from->fx_subsy != 0) abort(); #endif s = &(from->fx_addsy->sy_symbol.sy); to->address = ((char *) (from->fx_frag->fr_address + from->fx_where)) - ((char *) (&(from->fx_frag->fr_literal))); to->addend = from->fx_offset; /* If we know the symbol which we want to relocate to, turn this reloaction into a section relative. If this relocation is pcrelative, and we know the destination, we still want to keep the relocation - since the linker might relax some of the bytes, but it stops being pc relative and turns into an absolute relocation. */ if (s) { if ((s->flags & BSF_UNDEFINED) == 0) { to->section = s->section; /* We can refer directly to the value field here, rather than using S_GET_VALUE, because this is only called after do_symbols, which sets up the value field. */ to->addend += s->value; to->sym_ptr_ptr = 0; if (to->howto->pcrel_offset) { /* This is a pcrel relocation, the addend should be adjusted */ to->addend -= to->address + 1; } } else { to->section = 0; *ptrs = &(from->fx_addsy->sy_symbol.sy); to->sym_ptr_ptr = ptrs; if (to->howto->pcrel_offset) { /* This is a pcrel relocation, the addend should be adjusted */ to->addend -= to->address - 1; } } } else { to->section = 0; } ptrs++; from = from->fx_next; } /* attatch to the section */ section->orelocation = reloc_ptr_vector; section->reloc_count = nrelocs; section->flags |= SEC_LOAD; } } /* do the symbols.. */ static void DEFUN (do_symbols, (abfd), bfd * abfd) { extern symbolS *symbol_rootP; symbolS *ptr; asymbol **symbol_ptr_vec; asymbol *symbol_vec; unsigned int count = 0; unsigned int index; for (ptr = symbol_rootP; ptr != (symbolS *) NULL; ptr = ptr->sy_next) { if (SEG_NORMAL (ptr->sy_symbol.seg)) { ptr->sy_symbol.sy.section = (asection *) (segment_info[ptr->sy_symbol.seg].user_stuff); S_SET_VALUE (ptr, S_GET_VALUE (ptr) + ptr->sy_frag->fr_address); if (ptr->sy_symbol.sy.flags == 0) { ptr->sy_symbol.sy.flags = BSF_LOCAL; } } else { switch (ptr->sy_symbol.seg) { case SEG_ABSOLUTE: ptr->sy_symbol.sy.flags |= BSF_ABSOLUTE; ptr->sy_symbol.sy.section = 0; break; case SEG_UNKNOWN: ptr->sy_symbol.sy.flags = BSF_UNDEFINED; ptr->sy_symbol.sy.section = 0; break; default: abort (); } } ptr->sy_symbol.sy.value = S_GET_VALUE (ptr); count++; } symbol_ptr_vec = (asymbol **) malloc ((count + 1) * sizeof (asymbol *)); index = 0; for (ptr = symbol_rootP; ptr != (symbolS *) NULL; ptr = ptr->sy_next) { symbol_ptr_vec[index] = &(ptr->sy_symbol.sy); index++; } symbol_ptr_vec[index] = 0; abfd->outsymbols = symbol_ptr_vec; abfd->symcount = count; } /* The generic as->bfd converter. Other backends may have special case code */ void DEFUN_VOID (bfd_as_write_hook) { int i; for (i = SEG_E0; i < SEG_UNKNOWN; i++) { size_section (abfd, i); } for (i = SEG_E0; i < SEG_UNKNOWN; i++) fill_section (abfd, i); do_symbols (abfd); for (i = SEG_E0; i < SEG_UNKNOWN; i++) do_relocs_for (i); } S_SET_SEGMENT (x, y) symbolS *x; int y; { x->sy_symbol.seg = y; } S_IS_DEFINED (x) symbolS *x; { if (SEG_NORMAL (x->sy_symbol.seg)) { return 1; } switch (x->sy_symbol.seg) { case SEG_UNKNOWN: return 0; default: abort (); } } S_IS_EXTERNAL (x) { abort (); } S_GET_DESC (x) { abort (); } S_GET_SEGMENT (x) symbolS *x; { return x->sy_symbol.seg; } S_SET_EXTERNAL (x) symbolS *x; { x->sy_symbol.sy.flags |= BSF_GLOBAL | BSF_EXPORT; } S_SET_NAME (x, y) symbolS *x; char *y; { x->sy_symbol.sy.name = y; } S_GET_OTHER (x) { abort (); } S_IS_DEBUG (x) { abort (); } char * segment_name () { abort (); } void obj_read_begin_hook () { } static void obj_ieee_section (ignore) int ignore; { extern char *input_line_pointer; extern char is_end_of_line[]; char *p = input_line_pointer; char *s = p; int i; /* Look up the name, if it doesn't exist, make it */ while (*p && *p != ' ' && *p != ',' && !is_end_of_line[*p]) { p++; } for (i = SEG_E0; i < SEG_UNKNOWN; i++) { if (segment_info[i].hadone) { if (strncmp (segment_info[i].name, s, p - s) == 0) { goto ok; } } else break; } if (i == SEG_UNKNOWN) { as_bad ("too many sections"); return; } segment_info[i].hadone = 1; segment_info[i].name = malloc (p - s + 1); memcpy (segment_info[i].name, s, p - s); segment_info[i].name[p - s] = 0; ok: subseg_set (i, 0); while (!is_end_of_line[*p]) p++; input_line_pointer = p; } void cons (); void s_ignore (); void s_globl (); const pseudo_typeS obj_pseudo_table[] = { {"section", obj_ieee_section, 0}, {"data.b", cons, 1}, {"data.w", cons, 2}, {"data.l", cons, 4}, {"export", s_globl, 0}, {"option", s_ignore, 0}, {"end", s_ignore, 0}, {"import", s_ignore, 0}, {"sdata", stringer, 0}, 0, }; void obj_symbol_new_hook (symbolP) symbolS *symbolP; { symbolP->sy_symbol.sy.the_bfd = abfd; } #if 1 extern void DEFUN_VOID (write_object_file) { int i; struct frchain *frchain_ptr; struct frag *frag_ptr; abfd = bfd_openw (out_file_name, "ieee"); if (abfd == 0) { as_perror ("FATAL: Can't create %s", out_file_name); exit (42); } bfd_set_format (abfd, bfd_object); bfd_set_arch_mach (abfd, bfd_arch_h8300, 0); subseg_set (1, 0); subseg_set (2, 0); subseg_set (3, 0); for (frchain_ptr = frchain_root; frchain_ptr != (struct frchain *) NULL; frchain_ptr = frchain_ptr->frch_next) { /* Run through all the sub-segments and align them up. Also close any open frags. We tack a .fill onto the end of the frag chain so that any .align's size can be worked by looking at the next frag. */ subseg_set (frchain_ptr->frch_seg, frchain_ptr->frch_subseg); #ifndef SUB_SEGMENT_ALIGN #define SUB_SEGMENT_ALIGN(SEG) 2 #endif frag_align (SUB_SEGMENT_ALIGN (now_seg), 0); frag_wane (frag_now); frag_now->fr_fix = 0; know (frag_now->fr_next == NULL); } /* Now build one big frag chain for each segment, linked through fr_next. */ for (i = SEG_E0; i < SEG_UNKNOWN; i++) { fragS **prev_frag_ptr_ptr; struct frchain *next_frchain_ptr; /* struct frag **head_ptr = segment_info[i].frag_root;*/ segment_info[i].frag_root = segment_info[i].frchainP->frch_root; #if 0 /* Im not sure what this is for */ for (frchain_ptr = segment_info[i].frchainP->frch_root; frchain_ptr != (struct frchain *) NULL; frchain_ptr = frchain_ptr->frch_next) { *head_ptr = frchain_ptr; head_ptr = &frchain_ptr->next; } #endif } for (i = SEG_E0; i < SEG_UNKNOWN; i++) { relax_segment (segment_info[i].frag_root, i); } /* Now the addresses of the frags are correct within the segment */ bfd_as_write_hook (); bfd_close (abfd); } #endif H_SET_TEXT_SIZE (a, b) { abort (); } H_GET_TEXT_SIZE () { abort (); } H_SET_BSS_SIZE () { abort (); } H_SET_STRING_SIZE () { abort (); } H_SET_RELOCATION_SIZE () { abort (); } H_SET_MAGIC_NUMBER () { abort (); } H_GET_FILE_SIZE () { abort (); } H_GET_TEXT_RELOCATION_SIZE () { abort (); } /* end of obj-ieee.c */