home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OpenStep 4.2J (Developer)
/
os42jdev.iso
/
NextDeveloper
/
Source
/
GNU
/
cctools
/
as
/
layout.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-07-20
|
21KB
|
711 lines
/* layout.c (was part of write.c in original GAS version)
Copyright (C) 1986,1987 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 1, 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. */
#include <stdlib.h>
#include <string.h>
#include "stuff/round.h"
#include "as.h"
#include "sections.h"
#include "frags.h"
#include "symbols.h"
#include "fixes.h"
#include "messages.h"
#include "expr.h"
#include "md.h"
#include "obstack.h"
#ifdef SPARC
/* internal relocation types not to be emitted */
#define SPARC_RELOC_13 (127)
#define SPARC_RELOC_22 (126)
#endif
static void fixup_section(
fixS *fixP,
int nsect);
static void relax_section(
struct frag *section_frag_root,
int nsect);
static relax_addressT relax_align(
relax_addressT address,
long alignment);
static int is_down_range(
struct frag *f1,
struct frag *f2);
/*
* layout_addresses() is called after all the assembly code has been read and
* fragments, symbols and fixups have been created. This routine sets the
* address of the fragments and symbols. Then it does the fixups of the frags
* and prepares the fixes so relocation entries can be created from them.
*/
void
layout_addresses(
void)
{
struct frchain *frchainP;
fragS *fragP;
relax_addressT slide, tmp;
symbolS *symbolP;
if(frchain_root == NULL)
return;
/*
* If there is any current frag close it off.
*/
if(frag_now != NULL && frag_now->fr_fix == 0){
frag_now->fr_fix = obstack_next_free(&frags) -
frag_now->fr_literal;
frag_wane(frag_now);
}
/*
* For every section, add a last ".fill 0" frag that will later be used
* as the ending address of that section.
*/
for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
/*
* We must do the obstack_finish(), so the next object we put on
* obstack frags will not appear to start at the fr_literal of the
* current frag. Also, it ensures that the next object will begin
* on a address that is aligned correctly for the engine that runs
* the assembler.
*/
obstack_finish(&frags);
/*
* Make a fresh frag for the last frag.
*/
frag_now = (fragS *)obstack_alloc(&frags, SIZEOF_STRUCT_FRAG);
memset(frag_now, '\0', SIZEOF_STRUCT_FRAG);
frag_now->fr_next = NULL;
obstack_finish(&frags);
/*
* Append the new frag to current frchain.
*/
frchainP->frch_last->fr_next = frag_now;
frchainP->frch_last = frag_now;
frag_wane(frag_now);
}
/*
* Now set the relitive addresses of frags within the section by
* relaxing each section. That is all sections will start at address
* zero and addresses of the frags in that section will increase from
* there.
*/
for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
if((frchainP->frch_section.flags & SECTION_TYPE) == S_ZEROFILL)
continue;
/*
* This is done so in case md_estimate_size_before_relax() (called
* by relax_section) wants to make fixSs they are for this
* section.
*/
frchain_now = frchainP;
relax_section(frchainP->frch_root, frchainP->frch_nsect);
}
/*
* Now set the absolute addresses of all frags by sliding the frags in
* each non-zerofill section by the address ranges taken up by the
* sections before it.
*/
slide = 0;
for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
if((frchainP->frch_section.flags & SECTION_TYPE) == S_ZEROFILL)
continue;
slide = round(slide, 1 << frchainP->frch_section.align);
tmp = frchainP->frch_last->fr_address;
if(slide != 0){
for(fragP = frchainP->frch_root; fragP; fragP = fragP->fr_next){
fragP->fr_address += slide;
}
}
slide += tmp;
}
/*
* Now with the non-zerofill section addresses set set all of the
* addresses of the zerofill sections. Comming in the fr_address is
* the size of the section and going out it is the start address. This
* will make layout_symbols() work out naturally. The only funky thing
* is that section numbers do not end up in address order.
*/
for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
if((frchainP->frch_section.flags & SECTION_TYPE) != S_ZEROFILL)
continue;
slide = round(slide, 1 << frchainP->frch_section.align);
tmp = frchainP->frch_root->fr_address;
frchainP->frch_root->fr_address = slide;
frchainP->frch_last->fr_address = tmp + slide;
slide += tmp;
}
/*
* Set the symbol addresses based on there frag's address.
* First forward references are handled.
*/
for(symbolP = symbol_rootP; symbolP; symbolP = symbolP->sy_next){
if(symbolP->sy_forward != NULL){
if(symbolP->sy_nlist.n_type & N_STAB)
symbolP->sy_other = symbolP->sy_forward->sy_other;
symbolP->sy_value += symbolP->sy_forward->sy_value +
symbolP->sy_forward->sy_frag->fr_address;
symbolP->sy_forward = 0;
}
}
for(symbolP = symbol_rootP; symbolP; symbolP = symbolP->sy_next){
symbolP->sy_value += symbolP->sy_frag->fr_address;
}
/*
* At this point the addresses of frags now reflect addresses we use in
* the object file and the symbol values are correct.
* Scan the frags, converting any ".org"s and ".align"s to ".fill"s.
* Also converting any machine-dependent frags using md_convert_frag();
*/
for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
/*
* This is done so any fixes created by md_convert_frag() are for
* this section.
*/
frchain_now = frchainP;
for(fragP = frchainP->frch_root; fragP; fragP = fragP->fr_next){
switch(fragP->fr_type){
case rs_align:
case rs_org:
fragP->fr_type = rs_fill;
know(fragP->fr_var == 1);
know(fragP->fr_next != NULL);
fragP->fr_offset = fragP->fr_next->fr_address -
fragP->fr_address -
fragP->fr_fix;
break;
case rs_fill:
break;
case rs_machine_dependent:
md_convert_frag(fragP);
/*
* After md_convert_frag, we make the frag into a ".fill 0"
* md_convert_frag() should set up any fixSs and constants
* required.
*/
frag_wane(fragP);
break;
default:
BAD_CASE(fragP->fr_type);
break;
}
}
}
/*
* For each section do the fixups for the frags.
*/
for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
fixup_section(frchainP->frch_fix_root, frchainP->frch_nsect);
}
}
/*
* fixup_section() does the fixups of the frags and prepares the fixes so
* relocation entries can be created from them. The fixups cause the contents
* of the frag to have the value for the fixup expression. A fix structure that
* ends up with a non NULL fx_addsy will have a relocation entry created for it.
*/
static
void
fixup_section(
fixS *fixP,
int nsect)
{
symbolS *add_symbolP;
symbolS *sub_symbolP;
long value;
int size;
char *place;
long where;
char pcrel;
fragS *fragP;
int add_symbol_N_TYPE;
int add_symbol_nsect;
/*
* The general fix expression is "fx_addsy - fx_subsy + fx_offset".
* The goal is to put the result of this expression into the frag at
* "place" for size "size". The value of the expression is calculated
* in the variable "value" and starts with just the fx_offset.
*/
for( ; fixP != NULL; fixP = fixP->fx_next){
fragP = fixP->fx_frag;
know(fragP);
where = fixP->fx_where;
place = fragP->fr_literal + where;
size = fixP->fx_size;
add_symbolP = fixP->fx_addsy;
sub_symbolP = fixP->fx_subsy;
value = fixP->fx_offset;
pcrel = fixP->fx_pcrel;
add_symbol_N_TYPE = 0;
add_symbol_nsect = 0;
if(add_symbolP != NULL){
add_symbol_N_TYPE = add_symbolP->sy_type & N_TYPE;
if(add_symbol_N_TYPE == N_SECT)
add_symbol_nsect = add_symbolP->sy_other;
}
/*
* Is there a subtract symbol?
*/
if(sub_symbolP){
/* is it just -sym ? */
if(add_symbolP == NULL){
if(sub_symbolP->sy_type != N_ABS)
as_warn("Negative of non-absolute symbol %s",
sub_symbolP->sy_name);
value -= sub_symbolP->sy_value;
fixP->fx_subsy = NULL;
}
/*
* There are both an add symbol and a subtract symbol at this
* point.
*
* If both symbols are absolute then just calculate the
* value of the fix expression and no relocation entry will be
* needed.
*/
else if((sub_symbolP->sy_type & N_TYPE) == N_ABS &&
(add_symbolP->sy_type & N_TYPE) == N_ABS){
value += add_symbolP->sy_value - sub_symbolP->sy_value;
add_symbolP = NULL;
fixP->fx_addsy = NULL; /* no relocation entry */
fixP->fx_subsy = NULL;
}
/*
* If both symbols are defined in a section then calculate the
* value of the fix expression and let a section difference
* relocation entry be created.
*/
else if((sub_symbolP->sy_type & N_TYPE) == N_SECT &&
(add_symbolP->sy_type & N_TYPE) == N_SECT){
/*
* If we are allowed to use the new features that are
* incompatible with 3.2 then just calculate the value and
* let this create a SECTDIFF relocation type.
*/
if(flagseen['k']){
#ifdef SPARC
// special case dealing with assembler internal relocation entries
// SPARC_RELOC_13 and RELOC_22. The can not be output and must
// be resolved
if ((fixP->fx_r_type == SPARC_RELOC_13) ||
(fixP->fx_r_type == SPARC_RELOC_22)) {
if (sub_symbolP->sy_other == add_symbolP->sy_other) {
value += add_symbolP->sy_value -
sub_symbolP->sy_value;
add_symbolP = NULL;
fixP->fx_addsy = NULL; /* no relocation entry */
fixP->fx_subsy = NULL;
} else {
as_warn("Can't emit reloc type %u {-symbol \"%s\"} @ file "
"address %ld (mode?).", fixP->fx_r_type,
sub_symbolP->sy_name, fragP->fr_address + where);
}
} else
value += add_symbolP->sy_value - sub_symbolP->sy_value;
#else
value += add_symbolP->sy_value - sub_symbolP->sy_value;
#endif
goto down;
}
else{
/*
* This logic works only if the two symbols can't later
* be separated by scattered loading. To make sure that
* this can't happen we would have to make sure all
* symbols associated with addresses between these two
* symbols including the add symbol are of the Lx form
* and the -L flag is not see so they will not appear
* in the output (if they are not in the output then the
* link editor can't separate the chain of frags by
* scattered loading). Since this code does not make
* sure of this it is broken. But this is a known bug
* in the NeXT 3.2 and earilier releases so this code is
* if'ed !flagseen['k'] and will make it compatable with
* 3.2 and previous releases.
*/
if(sub_symbolP->sy_other == add_symbolP->sy_other){
value += add_symbolP->sy_value -
sub_symbolP->sy_value;
add_symbolP = NULL;
fixP->fx_addsy = NULL; /* no relocation entry */
fixP->fx_subsy = NULL;
}
else{
as_warn("Can't emit reloc {- symbol \"%s\"} @ file "
"address %ld (mode?).", sub_symbolP->sy_name,
fragP->fr_address + where);
}
}
}
/*
* If the subtract symbol is absolute subtract it's value from
* the fix expression and let a relocation entry get created
* that is not a section difference type.
*/
else if(sub_symbolP->sy_type == N_ABS){
value -= sub_symbolP->sy_value;
fixP->fx_subsy = NULL; /* no SECTDIFF relocation entry */
}
/*
* At this point we have something we can't generate a
* relocation entry for (two undefined symbols, etc.).
*/
else{
as_warn("Can't emit reloc {- symbol \"%s\"} @ file "
"address %ld.", sub_symbolP->sy_name,
fragP->fr_address + where);
}
}
/*
* If a there is an add symbol in the fixup expression then add
* the symbol value into the fixup expression's value.
*/
if(add_symbolP){
/*
* If this symbol is in this section and is pc-relative and we
* do not want to force a pc-relative relocation entry (to
* support scattered loading) then just calculate the value.
*/
if(add_symbol_nsect == nsect &&
pcrel && !(fixP->fx_pcrel_reloc)){
/*
* This fixup was made when the symbol's section was
* unknown, but it is now in this section. So we know how
* to do the address without relocation.
*/
value += add_symbolP->sy_value;
value -= size + where + fragP->fr_address;
pcrel = 0; /* Lie. Don't want further pcrel processing. */
fixP->fx_addsy = NULL; /* No relocations please. */
/*
* It would be nice to check that the address does not
* overflow.
* I didn't do this check because:
* + It is machine dependent in the general case (eg 32032)
* + Compiler output will never need this checking, so why
* slow down the usual case?
*/
}
else{
switch(add_symbol_N_TYPE){
case N_ABS:
value += add_symbolP->sy_value;
fixP->fx_addsy = NULL; /* no relocation entry */
add_symbolP = NULL;
break;
case N_SECT:
value += add_symbolP->sy_value;
break;
case N_UNDF:
break;
default:
BAD_CASE(add_symbol_N_TYPE);
break;
}
}
}
down:
/*
* If the fixup expression is pc-relative then the value of the pc
* will be added to the expression when the machine executes the
* the instruction so we adjust the fixup expression's value by
* subtracting off the pc value (where) and adjust for insn size.
*/
if(pcrel){
value -= size + where + fragP->fr_address;
if(add_symbolP == NULL){
fixP->fx_addsy = &abs_symbol; /* force relocation entry */
}
}
if((size == 1 && (value & 0xffffff00) &&
((value & 0xffffff80) != 0xffffff80)) ||
(size == 2 && (value & 0xffff8000) &&
((value & 0xffff8000) != 0xffff8000)))
as_warn("Fixup of %ld too large for field width of %d",
value, size);
/*
* Now place the fix expression's value in the place for the size.
* And save the fix expression's value to be used when creating
* a relocation entry if required.
*/
md_number_to_imm(place, value, size, fixP, nsect);
fixP->fx_value = value;
}
}
/*
* relax_section() here we set the fr_address values in the frags.
* After this, all frags in this segment have addresses that are correct
* relative to the section (that is the section starts at address zero).
* After all of the sections have been processed by this call and their sizes
* are know then they can be slid to their final address.
*/
static
void
relax_section(
struct frag *frag_root,
int nsect)
{
struct frag *fragP;
relax_addressT address;
long stretch; /* May be any size, 0 or negative. */
/* Cumulative number of addresses we have */
/* relaxed this pass. */
/* We may have relaxed more than one address. */
long stretched; /* Have we stretched on this pass? */
/* This is 'cuz stretch may be zero, when,
in fact some piece of code grew, and
another shrank. If a branch instruction
doesn't fit anymore, we need another pass */
const relax_typeS *this_type;
const relax_typeS *start_type;
relax_substateT next_state;
relax_substateT this_state;
long growth;
long was_address;
long offset;
symbolS *symbolP;
long target;
long after;
long aim;
growth = 0;
/*
* For each frag in segment count and store (a 1st guess of) fr_address.
*/
address = 0;
for(fragP = frag_root; fragP != NULL; fragP = fragP->fr_next){
fragP->fr_address = address;
address += fragP->fr_fix;
switch(fragP->fr_type){
case rs_fill:
address += fragP->fr_offset * fragP->fr_var;
break;
case rs_align:
address += relax_align(address, fragP->fr_offset);
break;
case rs_org:
/*
* Assume .org is nugatory. It will grow with 1st relax.
*/
break;
case rs_machine_dependent:
address += md_estimate_size_before_relax(fragP, nsect);
break;
default:
BAD_CASE(fragP->fr_type);
break;
}
}
/*
* Do relax().
* Make repeated passes over the chain of frags allowing each frag to
* grow if needed. On each pass each frag's address is incremented by
* the accumulated growth, kept in stretched. Passes are continued
* until there is no stretch on the previous pass.
*/
do{
stretch = 0;
stretched = 0;
for(fragP = frag_root; fragP != NULL; fragP = fragP->fr_next){
was_address = fragP->fr_address;
fragP->fr_address += stretch;
address = fragP->fr_address;
symbolP = fragP->fr_symbol;
offset = fragP->fr_offset;
switch(fragP->fr_type){
case rs_fill: /* .fill never relaxes. */
growth = 0;
break;
case rs_align:
growth = relax_align((relax_addressT)
(address + fragP->fr_fix), offset) -
relax_align((relax_addressT)
(was_address + fragP->fr_fix), offset);
break;
case rs_org:
target = offset;
if(symbolP != NULL){
know(((symbolP->sy_type & N_TYPE) == N_ABS) ||
((symbolP->sy_type & N_TYPE) == N_SECT));
know(symbolP->sy_frag);
know((symbolP->sy_type & N_TYPE) != N_ABS ||
symbolP->sy_frag == &zero_address_frag );
target += symbolP->sy_value +
symbolP->sy_frag->fr_address;
}
know(fragP->fr_next);
after = fragP->fr_next->fr_address;
/*
* Growth may be negative, but variable part of frag cannot
* have < 0 chars. That is, we can't .org backwards.
*/
growth = ((target - after ) > 0) ? (target - after) : 0;
growth -= stretch; /* This is an absolute growth factor */
break;
case rs_machine_dependent:
this_state = fragP->fr_subtype;
this_type = md_relax_table + this_state;
start_type = this_type;
target = offset;
if(symbolP){
know(((symbolP->sy_type & N_TYPE) == N_ABS) ||
((symbolP->sy_type & N_TYPE) == N_SECT));
know(symbolP->sy_frag);
know((symbolP->sy_type & N_TYPE) != N_ABS ||
symbolP->sy_frag == &zero_address_frag);
target += symbolP->sy_value +
symbolP->sy_frag->fr_address;
/*
* If frag has yet to be reached on this pass,
* assume it will move by STRETCH just as we did.
* If this is not so, it will be because some frag
* between grows, and that will force another pass.
*/
if(symbolP->sy_frag->fr_address >= was_address &&
is_down_range(fragP, symbolP->sy_frag))
target += stretch;
}
aim = target - address - fragP->fr_fix;
if(aim < 0){
/* Look backwards. */
for(next_state = this_type->rlx_more; next_state; ){
if(aim >= this_type->rlx_backward)
next_state = 0;
else{ /* Grow to next state. */
this_state = next_state;
this_type = md_relax_table + this_state;
next_state = this_type->rlx_more;
}
}
}
else{
/* Look forwards. */
for(next_state = this_type->rlx_more; next_state; ){
if(aim <= this_type->rlx_forward)
next_state = 0;
else{ /* Grow to next state. */
this_state = next_state;
this_type = md_relax_table + this_state;
next_state = this_type->rlx_more;
}
}
}
if((growth = this_type->rlx_length -start_type->rlx_length))
fragP->fr_subtype = this_state;
break;
default:
BAD_CASE(fragP->fr_type);
break;
}
if(growth) {
stretch += growth;
stretched++;
}
} /* For each frag in the segment. */
}while(stretched); /* Until nothing further to relax. */
/*
* We now have valid fr_address'es for each frag. All fr_address's
* are correct, relative to their own section. We have made all the
* fixS for this section that will be made.
*/
}
/*
* Relax_align. Advance location counter to next address that has 'alignment'
* lowest order bits all 0s.
*/
static
relax_addressT /* How many addresses does the .align take? */
relax_align(
relax_addressT address, /* Address now. */
long alignment) /* Alignment (binary). */
{
relax_addressT mask;
relax_addressT new_address;
mask = ~ ( (~0) << alignment );
new_address = (address + mask) & (~ mask);
return(new_address - address);
}
/*
* is_down_range() is used in relax_section() to determine it one fragment is
* after another to know if it will also be moved if the first is moved.
*/
static
int
is_down_range(
struct frag *f1,
struct frag *f2)
{
while(f1){
if(f1->fr_next == f2)
return(1);
f1 = f1->fr_next;
}
return(0);
}