638 lines
13 KiB
C
638 lines
13 KiB
C
/* 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 ();
|
|
}
|