#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
#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);
void
layout_addresses(
void)
{
struct frchain *frchainP;
fragS *fragP;
relax_addressT slide, tmp;
symbolS *symbolP;
if(frchain_root == NULL)
return;
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(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
obstack_finish(&frags);
frag_now = (fragS *)obstack_alloc(&frags, SIZEOF_STRUCT_FRAG);
memset(frag_now, '\0', SIZEOF_STRUCT_FRAG);
frag_now->fr_next = NULL;
obstack_finish(&frags);
frchainP->frch_last->fr_next = frag_now;
frchainP->frch_last = frag_now;
frag_wane(frag_now);
}
for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
if((frchainP->frch_section.flags & SECTION_TYPE) == S_ZEROFILL)
continue;
frchain_now = frchainP;
relax_section(frchainP->frch_root, frchainP->frch_nsect);
}
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;
}
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;
}
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;
}
for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
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;
if(fragP->fr_offset < 0){
as_warn("rs_org invalid, dot past value by %ld bytes",
fragP->fr_offset);
fragP->fr_offset = 0;
}
break;
case rs_fill:
break;
case rs_machine_dependent:
md_convert_frag(fragP);
frag_wane(fragP);
break;
default:
BAD_CASE(fragP->fr_type);
break;
}
}
}
for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
fixup_section(frchainP->frch_fix_root, frchainP->frch_nsect);
}
}
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;
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;
}
if(sub_symbolP){
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;
}
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;
fixP->fx_subsy = NULL;
}
else if((sub_symbolP->sy_type & N_TYPE) == N_SECT &&
(add_symbolP->sy_type & N_TYPE) == N_SECT){
#ifdef SPARC
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;
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 if(sub_symbolP->sy_type == N_ABS){
value -= sub_symbolP->sy_value;
fixP->fx_subsy = NULL;
}
else{
as_warn("Can't emit reloc {- symbol \"%s\"} @ file "
"address %ld.", sub_symbolP->sy_name,
fragP->fr_address + where);
}
}
if(add_symbolP){
if(add_symbol_nsect == nsect &&
pcrel && !(fixP->fx_pcrel_reloc)){
value += add_symbolP->sy_value;
value -= size + where + fragP->fr_address;
pcrel = 0;
fixP->fx_addsy = NULL;
}
else{
switch(add_symbol_N_TYPE){
case N_ABS:
if(add_symbolP->expression != NULL){
expressionS *exp;
exp = (expressionS *)add_symbolP->expression;
value +=
exp->X_add_symbol->sy_value -
exp->X_subtract_symbol->sy_value;
}
else
value += add_symbolP->sy_value;
fixP->fx_addsy = NULL;
add_symbolP = NULL;
break;
case N_SECT:
if((add_symbolP->sy_type & N_EXT) != N_EXT ||
add_symbol_N_TYPE != N_SECT ||
!is_section_coalesced(add_symbol_nsect))
value += add_symbolP->sy_value;
break;
case N_UNDF:
break;
default:
BAD_CASE(add_symbol_N_TYPE);
break;
}
}
}
down:
if(pcrel){
value -= size + where + fragP->fr_address;
if(add_symbolP == NULL){
fixP->fx_addsy = &abs_symbol;
}
}
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);
md_number_to_imm((unsigned char *)place, value, size, fixP, nsect);
fixP->fx_value = value;
}
}
static
void
relax_section(
struct frag *frag_root,
int nsect)
{
struct frag *fragP;
relax_addressT address;
long stretch;
long stretched;
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;
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:
break;
case rs_machine_dependent:
address += md_estimate_size_before_relax(fragP, nsect);
break;
default:
BAD_CASE(fragP->fr_type);
break;
}
}
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:
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 = ((target - after ) > 0) ? (target - after) : 0;
growth -= stretch;
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(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){
for(next_state = this_type->rlx_more; next_state; ){
if(aim >= this_type->rlx_backward)
next_state = 0;
else{
this_state = next_state;
this_type = md_relax_table + this_state;
next_state = this_type->rlx_more;
}
}
}
else{
for(next_state = this_type->rlx_more; next_state; ){
if(aim <= this_type->rlx_forward)
next_state = 0;
else{
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++;
}
}
}while(stretched);
}
static
relax_addressT
relax_align(
relax_addressT address,
long alignment)
{
relax_addressT mask;
relax_addressT new_address;
mask = ~ ( (~0) << alignment );
new_address = (address + mask) & (~ mask);
return(new_address - address);
}
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);
}