/* obj-format for ieee-695 records. Copyright 1991, 1992, 1993, 1994, 1997, 2000 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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) /* Address now. */ register relax_addressT address; /* Alignment (binary). */ register long alignment; { relax_addressT mask; relax_addressT new_address; mask = ~((~0) << alignment); new_address = (address + mask) & (~mask); return (new_address - address); } /* Calculate the size of the frag chain and create a bfd section to contain all of it. */ static void size_section (abfd, idx) bfd *abfd; 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: case rs_align_code: { addressT off; off = relax_align (size, frag->fr_offset); if (frag->fr_subtype != 0 && off > frag->fr_subtype) off = 0; size += off; } } 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 fill_section (abfd, idx) bfd *abfd; 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 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 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 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)); 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 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 (); } #ifndef segment_name char * segment_name () { abort (); } #endif 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 #ifndef SUB_SEGMENT_ALIGN #ifdef HANDLE_ALIGN /* The last subsegment gets an aligment corresponding to the alignment of the section. This allows proper nop-filling at the end of code-bearing sections. */ #define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) \ (!(FRCHAIN)->frch_next || (FRCHAIN)->frch_next->frch_seg != (SEG) \ ? get_recorded_alignment (SEG) : 0) #else #define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 2 #endif #endif extern 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 (EXIT_FAILURE); } 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); /* 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. */ for (frchain_ptr = frchain_root; frchain_ptr != (struct frchain *) NULL; frchain_ptr = frchain_ptr->frch_next) { int alignment; subseg_set (frchain_ptr->frch_seg, frchain_ptr->frch_subseg); alignment = SUB_SEGMENT_ALIGN (now_seg, frchain_ptr) #ifdef md_do_align md_do_align (alignment, (char *) NULL, 0, 0, alignment_done); #endif if (subseg_text_p (now_seg)) frag_align_code (alignment, 0); else frag_align (alignment, 0, 0); #ifdef md_do_align alignment_done: #endif 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; #if 0 struct frag **head_ptr = segment_info[i].frag_root; #endif segment_info[i].frag_root = segment_info[i].frchainP->frch_root; #if 0 /* I'm 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); /* Relaxation has completed. Freeze all syms. */ finalize_syms = 1; /* 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 (); }