From a3a33af390c3a99e00b20847a102e307cfb538c7 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 25 Mar 1994 22:37:55 +0000 Subject: [PATCH] Changes to support linker relaxing of embedded MIPS PIC code to use a five instruction sequence for funtion calls which are out of range of the bal instruction. * libecoff.h (struct ecoff_section_tdata): Define. (ecoff_section_data): Define. (ecoff_bfd_relax_section): Don't define. * ecoff.c (ecoff_final_link_debug_accumulate): Don't read or free the debugging information if it has already been read. (ecoff_indirect_link_order): Handle _cooked_size being different from _raw_size. Don't reread the contents or the relocs if they have already been read in. * coff-mips.c (mips_howto_table): Change bitsize of PCREL16 from 18 to 16. (PCREL16_EXPANSION_ADJUSTMENT): Define. (mips_relocate_refhi): Take adjust argument. (mips_relocate_section): Handle reloc offsets stored in section used_by_bfd field. Call mips_relax_pcrel16 to handle details of expanding an out of range PCREL16. Keep trace of adjustments required by expansions. Set s and unset h when converting a reloc from undefined to section. Change handling of PC relative relocs: if against a section, they are correct in the object file, if against an external symbol they are pcrel_offset. (mips_relax_section): New function. (mips_relax_pcrel16): New function. (ecoff_bfd_relax_section): Define. * coff-alpha.c (ecoff_bfd_relax_section): Define. * ecofflink.c (bfd_ecoff_debug_accumulate): Handle adjustments built by mips_relax_section when writing out addresses. * elf32-mips.c (mips_elf_read_ecoff_info): Clear adjust field. --- bfd/ChangeLog | 30 +++ bfd/coff-alpha.c | 44 ++++- bfd/coff-mips.c | 558 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- bfd/ecoff.c | 420 +++++++++++++++++++++++++++++++++-------- bfd/ecofflink.c | 128 +++++++++++-- bfd/elf32-mips.c | 76 ++++++-- bfd/libecoff.h | 211 +++++++++++++-------- 7 files changed, 1247 insertions(+), 220 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 528f826..7e657fd 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,35 @@ Fri Mar 25 17:10:45 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com) + Changes to support linker relaxing of embedded MIPS PIC code to + use a five instruction sequence for funtion calls which are out of + range of the bal instruction. + * libecoff.h (struct ecoff_section_tdata): Define. + (ecoff_section_data): Define. + (ecoff_bfd_relax_section): Don't define. + * ecoff.c (ecoff_final_link_debug_accumulate): Don't read or free + the debugging information if it has already been read. + (ecoff_indirect_link_order): Handle _cooked_size being different + from _raw_size. Don't reread the contents or the relocs if they + have already been read in. + * coff-mips.c (mips_howto_table): Change bitsize of PCREL16 from + 18 to 16. + (PCREL16_EXPANSION_ADJUSTMENT): Define. + (mips_relocate_refhi): Take adjust argument. + (mips_relocate_section): Handle reloc offsets stored in section + used_by_bfd field. Call mips_relax_pcrel16 to handle details of + expanding an out of range PCREL16. Keep trace of adjustments + required by expansions. Set s and unset h when converting a reloc + from undefined to section. Change handling of PC relative relocs: + if against a section, they are correct in the object file, if + against an external symbol they are pcrel_offset. + (mips_relax_section): New function. + (mips_relax_pcrel16): New function. + (ecoff_bfd_relax_section): Define. + * coff-alpha.c (ecoff_bfd_relax_section): Define. + * ecofflink.c (bfd_ecoff_debug_accumulate): Handle adjustments + built by mips_relax_section when writing out addresses. + * elf32-mips.c (mips_elf_read_ecoff_info): Clear adjust field. + * aoutx.h (NAME(aout,find_nearest_line)): The caller expects functionname_ptr to be set to a symbol name, so prepend symbol_leading_char. diff --git a/bfd/coff-alpha.c b/bfd/coff-alpha.c index 64ab840..4353078 100644 --- a/bfd/coff-alpha.c +++ b/bfd/coff-alpha.c @@ -715,16 +715,23 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order, bfd *input_bfd = link_order->u.indirect.section->owner; asection *input_section = link_order->u.indirect.section; size_t reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section); - arelent **reloc_vector = (arelent **) alloca (reloc_size); + arelent **reloc_vector = NULL; bfd *output_bfd = relocateable ? abfd : (bfd *) NULL; bfd_vma gp; boolean gp_undefined; bfd_vma stack[RELOC_STACKSIZE]; int tos = 0; + reloc_vector = (arelent **) malloc (reloc_size); + if (reloc_vector == NULL && reloc_size != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + if (! bfd_get_section_contents (input_bfd, input_section, data, (file_ptr) 0, input_section->_raw_size)) - return NULL; + goto error_return; /* The section size is not going to change. */ input_section->_cooked_size = input_section->_raw_size; @@ -733,7 +740,7 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order, if (bfd_canonicalize_reloc (input_bfd, input_section, reloc_vector, symbols) == 0) - return data; + goto successful_return; /* Get the GP value for the output BFD. */ gp_undefined = false; @@ -1080,20 +1087,20 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order, if (! ((*link_info->callbacks->undefined_symbol) (link_info, bfd_asymbol_name (*rel->sym_ptr_ptr), input_bfd, input_section, rel->address))) - return NULL; + goto error_return; break; case bfd_reloc_dangerous: if (! ((*link_info->callbacks->reloc_dangerous) (link_info, err, input_bfd, input_section, rel->address))) - return NULL; + goto error_return; break; case bfd_reloc_overflow: if (! ((*link_info->callbacks->reloc_overflow) (link_info, bfd_asymbol_name (*rel->sym_ptr_ptr), rel->howto->name, rel->addend, input_bfd, input_section, rel->address))) - return NULL; + goto error_return; break; case bfd_reloc_outofrange: default: @@ -1106,7 +1113,15 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order, if (tos != 0) abort (); + successful_return: + if (reloc_vector != NULL) + free (reloc_vector); return data; + + error_return: + if (reloc_vector != NULL) + free (reloc_vector); + return NULL; } /* Get the howto structure for a generic reloc type. */ @@ -1124,6 +1139,7 @@ alpha_bfd_reloc_type_lookup (abfd, code) alpha_type = ALPHA_R_REFLONG; break; case BFD_RELOC_64: + case BFD_RELOC_CTOR: alpha_type = ALPHA_R_REFQUAD; break; case BFD_RELOC_GPREL32: @@ -1658,17 +1674,26 @@ alpha_relocate_section (output_bfd, info, input_bfd, input_section, adjust the address of the reloc. */ if (! info->relocateable) { + bfd_vma mask; bfd_vma val; if (tos == 0) abort (); + /* Get the relocation mask. The separate steps and the + casts to bfd_vma are attempts to avoid a bug in the + Alpha OSF 1.3 C compiler. See reloc.c for more + details. */ + mask = 1; + mask <<= (bfd_vma) r_size; + mask -= 1; + /* FIXME: I don't know what kind of overflow checking, if any, should be done here. */ val = bfd_get_64 (input_bfd, contents + r_vaddr - input_section->vma); - val &=~ (((1 << r_size) - 1) << r_offset); - val |= (stack[--tos] & ((1 << r_size) - 1)) << r_offset; + val &=~ mask << (bfd_vma) r_offset; + val |= (stack[--tos] & mask) << (bfd_vma) r_offset; bfd_put_64 (input_bfd, val, contents + r_vaddr - input_section->vma); } @@ -1961,6 +1986,9 @@ static const struct ecoff_backend_data alpha_ecoff_backend_data = #define ecoff_bfd_get_relocated_section_contents \ alpha_ecoff_get_relocated_section_contents +/* Relaxing sections is generic. */ +#define ecoff_bfd_relax_section bfd_generic_relax_section + bfd_target ecoffalpha_little_vec = { "ecoff-littlealpha", /* name */ diff --git a/bfd/coff-mips.c b/bfd/coff-mips.c index a9a95e5..71a0cd5 100644 --- a/bfd/coff-mips.c +++ b/bfd/coff-mips.c @@ -77,10 +77,18 @@ static void mips_relocate_refhi PARAMS ((struct internal_reloc *refhi, bfd *input_bfd, asection *input_section, bfd_byte *contents, + size_t adjust, bfd_vma relocation)); static boolean mips_relocate_section PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, PTR)); +static boolean mips_relax_section PARAMS ((bfd *, asection *, + struct bfd_link_info *, + boolean *)); +static boolean mips_relax_pcrel16 PARAMS ((struct bfd_link_info *, bfd *, + asection *, + struct ecoff_link_hash_entry *, + bfd_byte *, bfd_vma)); /* ECOFF has COFF sections, but the debugging information is stored in a completely different format. ECOFF targets use some of the @@ -236,11 +244,45 @@ static reloc_howto_type mips_howto_table[] = true, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ - false) /* pcrel_offset */ + false), /* pcrel_offset */ + + /* This reloc is a Cygnus extension used when generating position + independent code for embedded systems. It represents a 16 bit PC + relative reloc rightshifted twice as used in the MIPS branch + instructions. */ + HOWTO (MIPS_R_PCREL16, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + mips_generic_reloc, /* special_function */ + "PCREL16", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + true) /* pcrel_offset */ }; #define MIPS_HOWTO_COUNT \ (sizeof mips_howto_table / sizeof mips_howto_table[0]) + +/* When the linker is doing relaxing, it may change a external PCREL16 + reloc. This typically represents an instruction like + bal foo + We change it to + .set noreorder + bal $L1 + lui $at,%hi(foo - $L1) + $L1: + addiu $at,%lo(foo - $L1) + addu $at,$at,$31 + jalr $at + PCREL16_EXPANSION_ADJUSTMENT is the number of bytes this changes the + instruction by. */ + +#define PCREL16_EXPANSION_ADJUSTMENT (4 * 4) /* See whether the magic number matches. */ @@ -357,7 +399,7 @@ mips_adjust_reloc_in (abfd, intern, rptr) const struct internal_reloc *intern; arelent *rptr; { - if (intern->r_type > MIPS_R_LITERAL) + if (intern->r_type > MIPS_R_PCREL16) abort (); if (! intern->r_extern @@ -715,6 +757,9 @@ mips_bfd_reloc_type_lookup (abfd, code) case BFD_RELOC_MIPS_LITERAL: mips_type = MIPS_R_LITERAL; break; + case BFD_RELOC_16_PCREL_S2: + mips_type = MIPS_R_PCREL16; + break; default: return (CONST struct reloc_howto_struct *) NULL; } @@ -729,12 +774,13 @@ mips_bfd_reloc_type_lookup (abfd, code) static void mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents, - relocation) + adjust, relocation) struct internal_reloc *refhi; struct internal_reloc *reflo; bfd *input_bfd; asection *input_section; bfd_byte *contents; + size_t adjust; bfd_vma relocation; { unsigned long insn; @@ -742,9 +788,9 @@ mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents, unsigned long vallo; insn = bfd_get_32 (input_bfd, - contents + refhi->r_vaddr - input_section->vma); + contents + adjust + refhi->r_vaddr - input_section->vma); vallo = (bfd_get_32 (input_bfd, - contents + reflo->r_vaddr - input_section->vma) + contents + adjust + reflo->r_vaddr - input_section->vma) & 0xffff); val = ((insn & 0xffff) << 16) + vallo; val += relocation; @@ -761,7 +807,7 @@ mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents, insn = (insn &~ 0xffff) | ((val >> 16) & 0xffff); bfd_put_32 (input_bfd, (bfd_vma) insn, - contents + refhi->r_vaddr - input_section->vma); + contents + adjust + refhi->r_vaddr - input_section->vma); } /* Relocate a section while linking a MIPS ECOFF file. */ @@ -780,9 +826,13 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, struct ecoff_link_hash_entry **sym_hashes; bfd_vma gp; boolean gp_undefined; + size_t adjust; + long *offsets; struct external_reloc *ext_rel; struct external_reloc *ext_rel_end; + unsigned int i; boolean got_reflo; + struct internal_reloc reflo_int_rel; BFD_ASSERT (input_bfd->xvec->header_byteorder_big_p == output_bfd->xvec->header_byteorder_big_p); @@ -842,12 +892,18 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, got_reflo = false; + adjust = 0; + + if (ecoff_section_data (input_bfd, input_section) == NULL) + offsets = NULL; + else + offsets = ecoff_section_data (input_bfd, input_section)->offsets; + ext_rel = (struct external_reloc *) external_relocs; ext_rel_end = ext_rel + input_section->reloc_count; - for (; ext_rel < ext_rel_end; ext_rel++) + for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++) { struct internal_reloc int_rel; - struct internal_reloc reflo_int_rel; bfd_vma addend; reloc_howto_type *howto; struct ecoff_link_hash_entry *h = NULL; @@ -955,6 +1011,54 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, } } + /* If we are relaxing, mips_relax_section may have set + offsets[i] to some value. A value of 1 means we must expand + a PC relative branch into a multi-instruction of sequence, + and any other value is an addend. */ + if (offsets != NULL + && offsets[i] != 0) + { + BFD_ASSERT (! info->relocateable); + BFD_ASSERT (int_rel.r_type == MIPS_R_PCREL16); + if (offsets[i] != 1) + { + BFD_ASSERT (! int_rel.r_extern); + addend += offsets[i]; + } + else + { + bfd_byte *here; + + BFD_ASSERT (int_rel.r_extern); + + /* Move the rest of the instructions up. */ + here = (contents + + adjust + + int_rel.r_vaddr + - input_section->vma); + memmove (here + PCREL16_EXPANSION_ADJUSTMENT, here, + (input_section->_raw_size + - (int_rel.r_vaddr - input_section->vma))); + + /* Generate the new instructions. */ + if (! mips_relax_pcrel16 (info, input_bfd, input_section, + h, here, + (input_section->output_section->vma + + input_section->output_offset + + (int_rel.r_vaddr + - input_section->vma) + + adjust))) + return false; + + /* We must adjust everything else up a notch. */ + adjust += PCREL16_EXPANSION_ADJUSTMENT; + + /* mips_relax_pcrel16 handles all the details of this + relocation. */ + continue; + } + } + if (info->relocateable) { /* We are generating relocateable output, and must convert @@ -963,7 +1067,6 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, { if (h->root.type == bfd_link_hash_defined) { - asection *hsec; const char *name; /* This symbol is defined in the output. Convert @@ -974,9 +1077,9 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, int_rel.r_extern = 0; /* Compute a new r_symndx value. */ - hsec = h->root.u.def.section; + s = h->root.u.def.section; name = bfd_get_section_name (output_bfd, - hsec->output_section); + s->output_section); int_rel.r_symndx = -1; switch (name[1]) @@ -1024,8 +1127,16 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, /* Add the section VMA and the symbol value. */ relocation = (h->root.u.def.value - + hsec->output_section->vma - + hsec->output_offset); + + s->output_section->vma + + s->output_offset); + + /* For a PC relative relocation, the object file + currently holds just the addend. We must adjust + by the address to get the right value. */ + if (howto->pc_relative) + relocation -= int_rel.r_vaddr - input_section->vma; + + h = NULL; } else { @@ -1056,6 +1167,14 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, relocation += addend; + /* Adjust a PC relative relocation by removing the reference + to the original address in the section and including the + reference to the new address. */ + if (howto->pc_relative) + relocation -= (input_section->output_section->vma + + input_section->output_offset + - input_section->vma); + /* Adjust the contents. */ if (relocation == 0) r = bfd_reloc_ok; @@ -1064,13 +1183,14 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, if (int_rel.r_type != MIPS_R_REFHI) r = _bfd_relocate_contents (howto, input_bfd, relocation, (contents + + adjust + int_rel.r_vaddr - input_section->vma)); else { mips_relocate_refhi (&int_rel, &reflo_int_rel, input_bfd, input_section, contents, - relocation); + adjust, relocation); r = bfd_reloc_ok; } } @@ -1115,10 +1235,11 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, + s->output_offset - s->vma); - /* Adjust a PC relative relocation by removing the - reference to the original source section. */ + /* A PC relative reloc is already correct in the object + file. Make it look like a pcrel_offset relocation by + adding in the start address. */ if (howto->pc_relative) - relocation += input_section->vma; + relocation += int_rel.r_vaddr + adjust; } if (int_rel.r_type != MIPS_R_REFHI) @@ -1126,13 +1247,16 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, input_bfd, input_section, contents, - int_rel.r_vaddr - input_section->vma, + (int_rel.r_vaddr + - input_section->vma + + adjust), relocation, addend); else { mips_relocate_refhi (&int_rel, &reflo_int_rel, input_bfd, - input_section, contents, relocation); + input_section, contents, adjust, + relocation); r = bfd_reloc_ok; } } @@ -1166,6 +1290,399 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section, return true; } +/* Relax a section when linking a MIPS ECOFF file. This is used for + embedded PIC code, which always uses PC relative branches which + only have an 18 bit range on MIPS. If a branch is not in range, we + generate a long instruction sequence to compensate. Each time we + find a branch to expand, we have to check all the others again to + make sure they are still in range. This is slow, but it only has + to be done when -relax is passed to the linker. + + This routine figures out which branches need to expand; the actual + expansion is done in mips_relocate_section when the section + contents are relocated. The information is stored in the offsets + field of the ecoff_section_tdata structure. An offset of 1 means + that the branch must be expanded into a multi-instruction PC + relative branch (such an offset will only occur for a PC relative + branch to an external symbol). Any other offset must be a multiple + of four, and is the amount to change the branch by (such an offset + will only occur for a PC relative branch within the same section). + + We do not modify the section relocs or contents themselves so that + if memory usage becomes an issue we can discard them and read them + again. The only information we must save in memory between this + routine and the mips_relocate_section routine is the table of + offsets. */ + +static boolean +mips_relax_section (abfd, sec, info, again) + bfd *abfd; + asection *sec; + struct bfd_link_info *info; + boolean *again; +{ + struct ecoff_section_tdata *section_tdata; + bfd_byte *contents = NULL; + long *offsets; + struct external_reloc *ext_rel; + struct external_reloc *ext_rel_end; + unsigned int i; + + /* Assume we are not going to need another pass. */ + *again = false; + + /* If we are not generating an ECOFF file, this is much too + confusing to deal with. */ + if (info->hash->creator->flavour != bfd_get_flavour (abfd)) + return true; + + /* If there are no relocs, there is nothing to do. */ + if (sec->reloc_count == 0) + return true; + + /* We are only interested in PC relative relocs, and why would there + ever be one from anything but the .text section? */ + if (strcmp (bfd_get_section_name (abfd, sec), ".text") != 0) + return true; + + /* Read in the relocs, if we haven't already got them. */ + section_tdata = ecoff_section_data (abfd, sec); + if (section_tdata == (struct ecoff_section_tdata *) NULL) + { + bfd_size_type external_reloc_size; + bfd_size_type external_relocs_size; + + sec->used_by_bfd = + (PTR) bfd_alloc_by_size_t (abfd, sizeof (struct ecoff_section_tdata)); + if (sec->used_by_bfd == NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + section_tdata = ecoff_section_data (abfd, sec); + section_tdata->contents = NULL; + section_tdata->offsets = NULL; + + external_reloc_size = ecoff_backend (abfd)->external_reloc_size; + external_relocs_size = external_reloc_size * sec->reloc_count; + + section_tdata->external_relocs = + (PTR) bfd_alloc (abfd, external_relocs_size); + if (section_tdata->external_relocs == NULL && external_relocs_size != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0 + || (bfd_read (section_tdata->external_relocs, 1, + external_relocs_size, abfd) + != external_relocs_size)) + goto error_return; + + /* We must initialize _cooked_size only the first time we are + called. */ + sec->_cooked_size = sec->_raw_size; + } + + contents = section_tdata->contents; + offsets = section_tdata->offsets; + + /* Look for any external PC relative relocs. Internal PC relative + relocs are already correct in the object file, so they certainly + can not overflow. */ + ext_rel = (struct external_reloc *) section_tdata->external_relocs; + ext_rel_end = ext_rel + sec->reloc_count; + for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++) + { + struct internal_reloc int_rel; + struct ecoff_link_hash_entry *h; + asection *hsec; + bfd_signed_vma relocation; + struct external_reloc *adj_ext_rel; + unsigned int adj_i; + unsigned long ext_count; + struct ecoff_link_hash_entry **adj_h_ptr; + struct ecoff_link_hash_entry **adj_h_ptr_end; + struct ecoff_value_adjust *adjust; + + /* If we have already expanded this reloc, we certainly don't + need to do it again. */ + if (offsets != (long *) NULL && offsets[i] == 1) + continue; + + /* Quickly check that this reloc is external PCREL16. */ + if (abfd->xvec->header_byteorder_big_p) + { + if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) == 0 + || (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG) + >> RELOC_BITS3_TYPE_SH_BIG) + != MIPS_R_PCREL16)) + continue; + } + else + { + if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) == 0 + || (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE) + >> RELOC_BITS3_TYPE_SH_LITTLE) + != MIPS_R_PCREL16)) + continue; + } + + mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel); + + h = ecoff_data (abfd)->sym_hashes[int_rel.r_symndx]; + if (h == (struct ecoff_link_hash_entry *) NULL) + abort (); + + if (h->root.type != bfd_link_hash_defined) + { + /* Just ignore undefined symbols. These will presumably + generate an error later in the link. */ + continue; + } + + /* Get the value of the symbol. */ + hsec = h->root.u.def.section; + relocation = (h->root.u.def.value + + hsec->output_section->vma + + hsec->output_offset); + + /* Subtract out the current address. */ + relocation -= (sec->output_section->vma + + sec->output_offset + + (int_rel.r_vaddr - sec->vma)); + + /* The addend is stored in the object file. In the normal case + of ``bal symbol'', the addend will be -4. It will only be + different in the case of ``bal symbol+constant''. To avoid + always reading in the section contents, we don't check the + addend in the object file (we could easily check the contents + if we happen to have already read them in, but I fear that + this could be confusing). This means we will screw up if + there is a branch to a symbol that is in range, but added to + a constant which puts it out of range; in such a case the + link will fail with a reloc overflow error. Since the + compiler will never generate such code, it should be easy + enough to work around it by changing the assembly code in the + source file. */ + relocation -= 4; + + /* Now RELOCATION is the number we want to put in the object + file. See whether it fits. */ + if (relocation >= -0x20000 && relocation < 0x20000) + continue; + + /* Now that we know this reloc needs work, which will rarely + happen, go ahead and grab the section contents. */ + if (contents == (bfd_byte *) NULL) + { + if (info->keep_memory) + contents = (bfd_byte *) bfd_alloc (abfd, sec->_raw_size); + else + contents = (bfd_byte *) malloc (sec->_raw_size); + if (contents == (bfd_byte *) NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + if (! bfd_get_section_contents (abfd, sec, (PTR) contents, + (file_ptr) 0, sec->_raw_size)) + goto error_return; + if (info->keep_memory) + section_tdata->contents = contents; + } + + /* We only support changing the bal instruction. It would be + possible to handle other PC relative branches, but some of + them (the conditional branches) would require a different + length instruction sequence which would complicate both this + routine and mips_relax_pcrel16. It could be written if + somebody felt it were important. Ignoring this reloc will + presumably cause a reloc overflow error later on. */ + if (bfd_get_32 (abfd, contents + int_rel.r_vaddr - sec->vma) + != 0x0411ffff) /* bgezal $0,. == bal . */ + continue; + + /* Bother. We need to expand this reloc, and we will need to + make another relaxation pass since this change may put other + relocs out of range. We need to examine the local branches + and we need to allocate memory to hold the offsets we must + add to them. We also need to adjust the values of all + symbols in the object file following this location. */ + + sec->_cooked_size += PCREL16_EXPANSION_ADJUSTMENT; + *again = true; + + if (offsets == (long *) NULL) + { + size_t size; + + size = sec->reloc_count * sizeof (long); + offsets = (long *) bfd_alloc_by_size_t (abfd, size); + if (offsets == (long *) NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + memset (offsets, 0, size); + section_tdata->offsets = offsets; + } + + offsets[i] = 1; + + /* Now look for all PC relative branches that cross this reloc + and adjust their offsets. We will turn the single branch + instruction into a four instruction sequence. In this loop + we are only interested in local PC relative branches. */ + adj_ext_rel = (struct external_reloc *) section_tdata->external_relocs; + for (adj_i = 0; adj_ext_rel < ext_rel_end; adj_ext_rel++, adj_i++) + { + struct internal_reloc adj_int_rel; + unsigned long insn; + bfd_vma dst; + + /* Quickly check that this reloc is internal PCREL16. */ + if (abfd->xvec->header_byteorder_big_p) + { + if ((adj_ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) != 0 + || (((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG) + >> RELOC_BITS3_TYPE_SH_BIG) + != MIPS_R_PCREL16)) + continue; + } + else + { + if ((adj_ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0 + || (((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE) + >> RELOC_BITS3_TYPE_SH_LITTLE) + != MIPS_R_PCREL16)) + continue; + } + + mips_ecoff_swap_reloc_in (abfd, (PTR) adj_ext_rel, &adj_int_rel); + + /* We are only interested in a PC relative reloc within this + section. FIXME: Cross section PC relative relocs may not + be handled correctly; does anybody care? */ + if (adj_int_rel.r_symndx != RELOC_SECTION_TEXT) + continue; + + /* Fetch the branch instruction. */ + insn = bfd_get_32 (abfd, contents + adj_int_rel.r_vaddr - sec->vma); + + /* Work out the destination address. */ + dst = (insn & 0xffff) << 2; + if ((dst & 0x20000) != 0) + dst -= 0x40000; + dst += adj_int_rel.r_vaddr + 4; + + /* If this branch crosses the branch we just decided to + expand, adjust the offset appropriately. */ + if (adj_int_rel.r_vaddr < int_rel.r_vaddr + && dst > int_rel.r_vaddr) + offsets[adj_i] += PCREL16_EXPANSION_ADJUSTMENT; + else if (adj_int_rel.r_vaddr > int_rel.r_vaddr + && dst <= int_rel.r_vaddr) + offsets[adj_i] -= PCREL16_EXPANSION_ADJUSTMENT; + } + + /* Find all symbols in this section defined by this object file + and adjust their values. Note that we decide whether to + adjust the value based on the value stored in the ECOFF EXTR + structure, because the value stored in the hash table may + have been changed by an earlier expanded reloc and thus may + no longer correctly indicate whether the symbol is before or + after the expanded reloc. */ + ext_count = ecoff_data (abfd)->debug_info.symbolic_header.iextMax; + adj_h_ptr = ecoff_data (abfd)->sym_hashes; + adj_h_ptr_end = adj_h_ptr + ext_count; + for (; adj_h_ptr < adj_h_ptr_end; adj_h_ptr++) + { + struct ecoff_link_hash_entry *adj_h; + + adj_h = *adj_h_ptr; + if (adj_h != (struct ecoff_link_hash_entry *) NULL + && adj_h->root.type == bfd_link_hash_defined + && adj_h->root.u.def.section == sec + && adj_h->esym.asym.value > int_rel.r_vaddr) + adj_h->root.u.def.value += PCREL16_EXPANSION_ADJUSTMENT; + } + + /* Add an entry to the symbol value adjust list. This is used + by bfd_ecoff_debug_accumulate to adjust the values of + internal symbols and FDR's. */ + adjust = ((struct ecoff_value_adjust *) + bfd_alloc (abfd, sizeof (struct ecoff_value_adjust))); + if (adjust == (struct ecoff_value_adjust *) NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + adjust->start = int_rel.r_vaddr; + adjust->end = sec->vma + sec->_raw_size; + adjust->adjust = PCREL16_EXPANSION_ADJUSTMENT; + + adjust->next = ecoff_data (abfd)->debug_info.adjust; + ecoff_data (abfd)->debug_info.adjust = adjust; + } + + if (contents != (bfd_byte *) NULL && ! info->keep_memory) + free (contents); + + return true; + + error_return: + if (contents != (bfd_byte *) NULL && ! info->keep_memory) + free (contents); + return false; +} + +/* This routine is called from mips_relocate_section when a PC + relative reloc must be expanded into the five instruction sequence. + It handles all the details of the expansion, including resolving + the reloc. */ + +static boolean +mips_relax_pcrel16 (info, input_bfd, input_section, h, location, address) + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + struct ecoff_link_hash_entry *h; + bfd_byte *location; + bfd_vma address; +{ + bfd_vma relocation; + + /* 0x0411ffff is bgezal $0,. == bal . */ + BFD_ASSERT (bfd_get_32 (input_bfd, location) == 0x0411ffff); + + /* We need to compute the distance between the symbol and the + current address plus eight. */ + relocation = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + relocation -= address + 8; + + /* If the lower half is negative, increment the upper 16 half. */ + if ((relocation & 0x8000) != 0) + relocation += 0x10000; + + bfd_put_32 (input_bfd, 0x04110001, location); /* bal .+8 */ + bfd_put_32 (input_bfd, + 0x3c010000 | ((relocation >> 16) & 0xffff), /* lui $at,XX */ + location + 4); + bfd_put_32 (input_bfd, + 0x24210000 | (relocation & 0xffff), /* addiu $at,$at,XX */ + location + 8); + bfd_put_32 (input_bfd, 0x003f0821, location + 12); /* addu $at,$at,$ra */ + bfd_put_32 (input_bfd, 0x0020f809, location + 16); /* jalr $at */ + + return true; +} + /* This is the ECOFF backend structure. The backend field of the target vector points to this. */ @@ -1256,6 +1773,9 @@ static const struct ecoff_backend_data mips_ecoff_backend_data = #define ecoff_bfd_get_relocated_section_contents \ bfd_generic_get_relocated_section_contents +/* Relaxing sections is MIPS specific. */ +#define ecoff_bfd_relax_section mips_relax_section + /* Core file support is usually traditional (but note that Irix uses irix-core.c). */ #define ecoff_core_file_p _bfd_dummy_target diff --git a/bfd/ecoff.c b/bfd/ecoff.c index 745d46a..5665a99 100644 --- a/bfd/ecoff.c +++ b/bfd/ecoff.c @@ -371,13 +371,16 @@ ecoff_styp_to_sec_flags (abfd, hdr) } else if ((styp_flags & STYP_DATA) || (styp_flags & STYP_RDATA) - || (styp_flags & STYP_SDATA)) + || (styp_flags & STYP_SDATA) + || styp_flags == STYP_PDATA + || styp_flags == STYP_XDATA) { if (sec_flags & SEC_NEVER_LOAD) sec_flags |= SEC_DATA | SEC_SHARED_LIBRARY; else sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC; - if (styp_flags & STYP_RDATA) + if ((styp_flags & STYP_RDATA) + || styp_flags == STYP_PDATA) sec_flags |= SEC_READONLY; } else if ((styp_flags & STYP_BSS) @@ -385,7 +388,7 @@ ecoff_styp_to_sec_flags (abfd, hdr) { sec_flags |= SEC_ALLOC; } - else if (styp_flags & STYP_INFO) + else if ((styp_flags & STYP_INFO) || styp_flags == STYP_COMMENT) { sec_flags |= SEC_NEVER_LOAD; } @@ -3650,7 +3653,7 @@ ecoff_link_check_archive_element (abfd, info, pneeded) external_ext_size = backend->debug_swap.external_ext_size; esize = symhdr->iextMax * external_ext_size; external_ext = (PTR) malloc (esize); - if (external_ext == NULL) + if (external_ext == NULL && esize != 0) { bfd_set_error (bfd_error_no_memory); goto error_return; @@ -3661,7 +3664,7 @@ ecoff_link_check_archive_element (abfd, info, pneeded) goto error_return; ssext = (char *) malloc (symhdr->issExtMax); - if (ssext == NULL) + if (ssext == NULL && symhdr->issExtMax != 0) { bfd_set_error (bfd_error_no_memory); goto error_return; @@ -3774,7 +3777,7 @@ ecoff_link_add_object_symbols (abfd, info) external_ext_size = ecoff_backend (abfd)->debug_swap.external_ext_size; esize = symhdr->iextMax * external_ext_size; external_ext = (PTR) malloc (esize); - if (external_ext == NULL) + if (external_ext == NULL && esize != 0) { bfd_set_error (bfd_error_no_memory); goto error_return; @@ -3785,7 +3788,7 @@ ecoff_link_add_object_symbols (abfd, info) goto error_return; ssext = (char *) malloc (symhdr->issExtMax); - if (ssext == NULL) + if (ssext == NULL && symhdr->issExtMax != 0) { bfd_set_error (bfd_error_no_memory); goto error_return; @@ -3972,7 +3975,7 @@ ecoff_link_add_externals (abfd, info, external_ext, ssext) if (! (_bfd_generic_link_add_one_symbol (info, abfd, name, BSF_GLOBAL, section, value, - (const char *) NULL, true, true, backend->constructor_bitsize, + (const char *) NULL, true, true, (struct bfd_link_hash_entry **) &h))) return false; @@ -4006,6 +4009,9 @@ static boolean ecoff_link_write_external static boolean ecoff_indirect_link_order PARAMS ((bfd *, struct bfd_link_info *, asection *, struct bfd_link_order *)); +static boolean ecoff_reloc_link_order + PARAMS ((bfd *, struct bfd_link_info *, asection *, + struct bfd_link_order *)); /* ECOFF final link routine. This looks through all the input BFDs and gathers together all the debugging information, and then @@ -4111,6 +4117,9 @@ ecoff_bfd_final_link (abfd, info) p = p->next) if (p->type == bfd_indirect_link_order) o->reloc_count += p->u.indirect.section->reloc_count; + else if (p->type == bfd_section_reloc_link_order + || p->type == bfd_symbol_reloc_link_order) + ++o->reloc_count; } } @@ -4189,6 +4198,12 @@ ecoff_bfd_final_link (abfd, info) if (! ecoff_indirect_link_order (abfd, info, o, p)) return false; } + else if (p->type == bfd_section_reloc_link_order + || p->type == bfd_symbol_reloc_link_order) + { + if (! ecoff_reloc_link_order (abfd, info, o, p)) + return false; + } else { if (! _bfd_default_link_order (abfd, info, o, p)) @@ -4241,16 +4256,22 @@ ecoff_final_link_debug_accumulate (output_bfd, input_bfd, info, handle) } \ } - READ (line, cbLineOffset, cbLine, sizeof (unsigned char), unsigned char *); - READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, PTR); - READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, PTR); - READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, PTR); - READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, PTR); - READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext), - union aux_ext *); - READ (ss, cbSsOffset, issMax, sizeof (char), char *); - READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, PTR); - READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, PTR); + /* If raw_syments is not NULL, then the data was already by read by + ecoff_slurp_symbolic_info. */ + if (ecoff_data (input_bfd)->raw_syments == NULL) + { + READ (line, cbLineOffset, cbLine, sizeof (unsigned char), + unsigned char *); + READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, PTR); + READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, PTR); + READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, PTR); + READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, PTR); + READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext), + union aux_ext *); + READ (ss, cbSsOffset, issMax, sizeof (char), char *); + READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, PTR); + READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, PTR); + } #undef READ /* We do not read the external strings or the external symbols. */ @@ -4261,36 +4282,39 @@ ecoff_final_link_debug_accumulate (output_bfd, input_bfd, info, handle) input_bfd, debug, swap, info)); return_something: - if (debug->line != NULL) - free (debug->line); - if (debug->external_dnr != NULL) - free (debug->external_dnr); - if (debug->external_pdr != NULL) - free (debug->external_pdr); - if (debug->external_sym != NULL) - free (debug->external_sym); - if (debug->external_opt != NULL) - free (debug->external_opt); - if (debug->external_aux != NULL) - free (debug->external_aux); - if (debug->ss != NULL) - free (debug->ss); - if (debug->external_fdr != NULL) - free (debug->external_fdr); - if (debug->external_rfd != NULL) - free (debug->external_rfd); - - /* Make sure we don't accidentally follow one of these pointers on - to the stack. */ - debug->line = NULL; - debug->external_dnr = NULL; - debug->external_pdr = NULL; - debug->external_sym = NULL; - debug->external_opt = NULL; - debug->external_aux = NULL; - debug->ss = NULL; - debug->external_fdr = NULL; - debug->external_rfd = NULL; + if (ecoff_data (input_bfd)->raw_syments == NULL) + { + if (debug->line != NULL) + free (debug->line); + if (debug->external_dnr != NULL) + free (debug->external_dnr); + if (debug->external_pdr != NULL) + free (debug->external_pdr); + if (debug->external_sym != NULL) + free (debug->external_sym); + if (debug->external_opt != NULL) + free (debug->external_opt); + if (debug->external_aux != NULL) + free (debug->external_aux); + if (debug->ss != NULL) + free (debug->ss); + if (debug->external_fdr != NULL) + free (debug->external_fdr); + if (debug->external_rfd != NULL) + free (debug->external_rfd); + + /* Make sure we don't accidentally follow one of these pointers + into freed memory. */ + debug->line = NULL; + debug->external_dnr = NULL; + debug->external_pdr = NULL; + debug->external_sym = NULL; + debug->external_opt = NULL; + debug->external_aux = NULL; + debug->ss = NULL; + debug->external_fdr = NULL; + debug->external_rfd = NULL; + } return ret; } @@ -4318,9 +4342,42 @@ ecoff_link_write_external (h, data) h->esym.reserved = 0; h->esym.ifd = ifdNil; h->esym.asym.value = 0; - /* FIXME: we can do better than this for st and sc. */ h->esym.asym.st = stGlobal; - h->esym.asym.sc = scAbs; + + if (h->root.type != bfd_link_hash_defined) + h->esym.asym.sc = scAbs; + else + { + asection *output_section; + const char *name; + + output_section = h->root.u.def.section->output_section; + name = bfd_section_name (output_section->owner, output_section); + + if (strcmp (name, _TEXT) == 0) + h->esym.asym.sc = scText; + else if (strcmp (name, _DATA) == 0) + h->esym.asym.sc = scData; + else if (strcmp (name, _SDATA) == 0) + h->esym.asym.sc = scSData; + else if (strcmp (name, _RDATA) == 0) + h->esym.asym.sc = scRData; + else if (strcmp (name, _BSS) == 0) + h->esym.asym.sc = scBss; + else if (strcmp (name, _SBSS) == 0) + h->esym.asym.sc = scSBss; + else if (strcmp (name, _INIT) == 0) + h->esym.asym.sc = scInit; + else if (strcmp (name, _FINI) == 0) + h->esym.asym.sc = scFini; + else if (strcmp (name, _PDATA) == 0) + h->esym.asym.sc = scPData; + else if (strcmp (name, _XDATA) == 0) + h->esym.asym.sc = scXData; + else + h->esym.asym.sc = scAbs; + } + h->esym.asym.reserved = 0; h->esym.asym.index = indexNil; } @@ -4343,9 +4400,9 @@ ecoff_link_write_external (h, data) abort (); case bfd_link_hash_undefined: case bfd_link_hash_weak: - if (h->esym.asym.st != scUndefined - && h->esym.asym.st != scSUndefined) - h->esym.asym.st = scUndefined; + if (h->esym.asym.sc != scUndefined + && h->esym.asym.sc != scSUndefined) + h->esym.asym.sc = scUndefined; break; case bfd_link_hash_defined: if (h->esym.asym.sc == scUndefined @@ -4394,7 +4451,9 @@ ecoff_indirect_link_order (output_bfd, info, output_section, link_order) { asection *input_section; bfd *input_bfd; - bfd_size_type input_size; + struct ecoff_section_tdata *section_tdata; + bfd_size_type raw_size; + bfd_size_type cooked_size; bfd_byte *contents = NULL; bfd_size_type external_reloc_size; bfd_size_type external_relocs_size; @@ -4407,37 +4466,63 @@ ecoff_indirect_link_order (output_bfd, info, output_section, link_order) input_section = link_order->u.indirect.section; input_bfd = input_section->owner; + section_tdata = ecoff_section_data (input_bfd, input_section); + + raw_size = input_section->_raw_size; + cooked_size = input_section->_cooked_size; + if (cooked_size == 0) + cooked_size = raw_size; BFD_ASSERT (input_section->output_section == output_section); BFD_ASSERT (input_section->output_offset == link_order->offset); - BFD_ASSERT (bfd_section_size (input_bfd, input_section) == link_order->size); - - /* Get the section contents. */ - input_size = bfd_section_size (input_bfd, input_section); - contents = (bfd_byte *) malloc (input_size); - if (contents == NULL) + BFD_ASSERT (cooked_size == link_order->size); + + /* Get the section contents. We allocate memory for the larger of + the size before relocating and the size after relocating. */ + contents = (bfd_byte *) malloc (raw_size >= cooked_size + ? raw_size + : cooked_size); + if (contents == NULL && raw_size != 0) { bfd_set_error (bfd_error_no_memory); goto error_return; } - if (! bfd_get_section_contents (input_bfd, input_section, (PTR) contents, - (file_ptr) 0, input_size)) - goto error_return; - /* Get the relocs. */ + /* If we are relaxing, the contents may have already been read into + memory, in which case we copy them into our new buffer. We don't + simply reuse the old buffer in case cooked_size > raw_size. */ + if (section_tdata != (struct ecoff_section_tdata *) NULL + && section_tdata->contents != (bfd_byte *) NULL) + memcpy (contents, section_tdata->contents, raw_size); + else + { + if (! bfd_get_section_contents (input_bfd, input_section, + (PTR) contents, + (file_ptr) 0, raw_size)) + goto error_return; + } + + /* Get the relocs. If we are relaxing MIPS code, they will already + have been read in. Otherwise, we read them in now. */ external_reloc_size = ecoff_backend (input_bfd)->external_reloc_size; external_relocs_size = external_reloc_size * input_section->reloc_count; - external_relocs = (PTR) malloc (external_relocs_size); - if (external_relocs == NULL) + + if (section_tdata != (struct ecoff_section_tdata *) NULL) + external_relocs = section_tdata->external_relocs; + else { - bfd_set_error (bfd_error_no_memory); - goto error_return; - } + external_relocs = (PTR) malloc (external_relocs_size); + if (external_relocs == NULL && external_relocs_size != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } - if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0 - || (bfd_read (external_relocs, 1, external_relocs_size, input_bfd) - != external_relocs_size)) - goto error_return; + if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0 + || (bfd_read (external_relocs, 1, external_relocs_size, input_bfd) + != external_relocs_size)) + goto error_return; + } /* Relocate the section contents. */ if (! ((*ecoff_backend (input_bfd)->relocate_section) @@ -4450,7 +4535,7 @@ ecoff_indirect_link_order (output_bfd, info, output_section, link_order) output_section, (PTR) contents, input_section->output_offset, - input_size)) + cooked_size)) goto error_return; /* If we are producing relocateable output, the relocs were @@ -4471,14 +4556,197 @@ ecoff_indirect_link_order (output_bfd, info, output_section, link_order) if (contents != NULL) free (contents); - if (external_relocs != NULL) + if (external_relocs != NULL && section_tdata == NULL) free (external_relocs); return true; error_return: if (contents != NULL) free (contents); - if (external_relocs != NULL) + if (external_relocs != NULL && section_tdata == NULL) free (external_relocs); return false; } + +/* Generate a reloc when linking an ECOFF file. This is a reloc + requested by the linker, and does come from any input file. This + is used to build constructor and destructor tables when linking + with -Ur. */ + +static boolean +ecoff_reloc_link_order (output_bfd, info, output_section, link_order) + bfd *output_bfd; + struct bfd_link_info *info; + asection *output_section; + struct bfd_link_order *link_order; +{ + arelent rel; + struct internal_reloc in; + bfd_size_type external_reloc_size; + bfd_byte *rbuf; + boolean ok; + + /* We set up an arelent to pass to the backend adjust_reloc_out + routine. */ + rel.address = link_order->offset; + + rel.howto = bfd_reloc_type_lookup (output_bfd, link_order->u.reloc.p->reloc); + if (rel.howto == (const reloc_howto_type *) NULL) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + if (link_order->type == bfd_section_reloc_link_order) + rel.sym_ptr_ptr = link_order->u.reloc.p->u.section->symbol_ptr_ptr; + else + { + /* We can't set up a reloc against a symbol correctly, because + we have no asymbol structure. Currently no adjust_reloc_out + routine cases. */ + rel.sym_ptr_ptr = (asymbol **) NULL; + } + + /* All ECOFF relocs are in-place. Put the addend into the object + file. */ + + BFD_ASSERT (rel.howto->partial_inplace); + if (link_order->u.reloc.p->addend != 0) + { + bfd_size_type size; + bfd_reloc_status_type rstat; + bfd_byte *buf; + boolean ok; + + size = bfd_get_reloc_size (rel.howto); + buf = (bfd_byte *) bfd_zmalloc (size); + if (buf == (bfd_byte *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + rstat = _bfd_relocate_contents (rel.howto, output_bfd, + link_order->u.reloc.p->addend, buf); + switch (rstat) + { + case bfd_reloc_ok: + break; + default: + case bfd_reloc_outofrange: + abort (); + case bfd_reloc_overflow: + if (! ((*info->callbacks->reloc_overflow) + (info, + (link_order->type == bfd_section_reloc_link_order + ? bfd_section_name (output_bfd, + link_order->u.reloc.p->u.section) + : link_order->u.reloc.p->u.name), + rel.howto->name, link_order->u.reloc.p->addend, + (bfd *) NULL, (asection *) NULL, (bfd_vma) 0))) + { + free (buf); + return false; + } + break; + } + ok = bfd_set_section_contents (output_bfd, output_section, (PTR) buf, + (file_ptr) link_order->offset, size); + free (buf); + if (! ok) + return false; + } + + rel.addend = 0; + + /* Move the information into a internal_reloc structure. */ + in.r_vaddr = (rel.address + + bfd_get_section_vma (output_bfd, output_section)); + in.r_type = rel.howto->type; + + if (link_order->type == bfd_symbol_reloc_link_order) + { + struct ecoff_link_hash_entry *h; + + h = ecoff_link_hash_lookup (ecoff_hash_table (info), + link_order->u.reloc.p->u.name, + false, false, true); + if (h != (struct ecoff_link_hash_entry *) NULL + && h->indx != -1) + in.r_symndx = h->indx; + else + { + if (! ((*info->callbacks->unattached_reloc) + (info, link_order->u.reloc.p->u.name, (bfd *) NULL, + (asection *) NULL, (bfd_vma) 0))) + return false; + in.r_symndx = 0; + } + in.r_extern = 1; + } + else + { + CONST char *name; + + name = bfd_get_section_name (output_bfd, + link_order->u.reloc.p->u.section); + if (strcmp (name, ".text") == 0) + in.r_symndx = RELOC_SECTION_TEXT; + else if (strcmp (name, ".rdata") == 0) + in.r_symndx = RELOC_SECTION_RDATA; + else if (strcmp (name, ".data") == 0) + in.r_symndx = RELOC_SECTION_DATA; + else if (strcmp (name, ".sdata") == 0) + in.r_symndx = RELOC_SECTION_SDATA; + else if (strcmp (name, ".sbss") == 0) + in.r_symndx = RELOC_SECTION_SBSS; + else if (strcmp (name, ".bss") == 0) + in.r_symndx = RELOC_SECTION_BSS; + else if (strcmp (name, ".init") == 0) + in.r_symndx = RELOC_SECTION_INIT; + else if (strcmp (name, ".lit8") == 0) + in.r_symndx = RELOC_SECTION_LIT8; + else if (strcmp (name, ".lit4") == 0) + in.r_symndx = RELOC_SECTION_LIT4; + else if (strcmp (name, ".xdata") == 0) + in.r_symndx = RELOC_SECTION_XDATA; + else if (strcmp (name, ".pdata") == 0) + in.r_symndx = RELOC_SECTION_PDATA; + else if (strcmp (name, ".fini") == 0) + in.r_symndx = RELOC_SECTION_FINI; + else if (strcmp (name, ".lita") == 0) + in.r_symndx = RELOC_SECTION_LITA; + else if (strcmp (name, "*ABS*") == 0) + in.r_symndx = RELOC_SECTION_ABS; + else + abort (); + in.r_extern = 0; + } + + /* Let the BFD backend adjust the reloc. */ + (*ecoff_backend (output_bfd)->adjust_reloc_out) (output_bfd, &rel, &in); + + /* Get some memory and swap out the reloc. */ + external_reloc_size = ecoff_backend (output_bfd)->external_reloc_size; + rbuf = (bfd_byte *) malloc (external_reloc_size); + if (rbuf == (bfd_byte *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + (*ecoff_backend (output_bfd)->swap_reloc_out) (output_bfd, &in, (PTR) rbuf); + + ok = (bfd_seek (output_bfd, + (output_section->rel_filepos + + output_section->reloc_count * external_reloc_size), + SEEK_SET) == 0 + && (bfd_write ((PTR) rbuf, 1, external_reloc_size, output_bfd) + == external_reloc_size)); + + if (ok) + ++output_section->reloc_count; + + free (rbuf); + + return ok; +} diff --git a/bfd/ecofflink.c b/bfd/ecofflink.c index a4507ea..4b14dcd 100644 --- a/bfd/ecofflink.c +++ b/bfd/ecofflink.c @@ -519,10 +519,17 @@ bfd_ecoff_debug_accumulate (handle, output_bfd, output_debug, output_swap, hash reduces the chance that we will merge symbol information that should not be merged. */ name = input_debug->ss + fdr.issBase + fdr.rss; - lookup = (char *) alloca (strlen (name) + 20); + + lookup = (char *) malloc (strlen (name) + 20); + if (lookup == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } sprintf (lookup, "%s %lx", name, fdr.csym); fh = string_hash_lookup (&ainfo->fdr_hash, lookup, true, true); + free (lookup); if (fh == (struct string_hash_entry *) NULL) return false; @@ -600,6 +607,22 @@ bfd_ecoff_debug_accumulate (handle, output_bfd, output_debug, output_swap, else (*input_swap->swap_fdr_in) (input_bfd, (PTR) fdr_ptr, &fdr); + /* Adjust the FDR address for any changes that may have been + made by relaxing. */ + if (input_debug->adjust != (struct ecoff_value_adjust *) NULL) + { + bfd_vma adr; + struct ecoff_value_adjust *adjust; + + adr = fdr.adr; + for (adjust = input_debug->adjust; + adjust != (struct ecoff_value_adjust *) NULL; + adjust = adjust->next) + if (adr >= adjust->start + && adr < adjust->end) + fdr.adr += adjust->adjust; + } + /* FIXME: It is conceivable that this FDR points to the .init or .fini section, in which case this will not do the right thing. */ @@ -641,6 +664,19 @@ bfd_ecoff_debug_accumulate (handle, output_bfd, output_debug, output_swap, case stLabel: case stProc: case stStaticProc: + if (input_debug->adjust != (struct ecoff_value_adjust *) NULL) + { + bfd_vma value; + struct ecoff_value_adjust *adjust; + + value = internal_sym.value; + for (adjust = input_debug->adjust; + adjust != (struct ecoff_value_adjust *) NULL; + adjust = adjust->next) + if (value >= adjust->start + && value < adjust->end) + internal_sym.value += adjust->adjust; + } internal_sym.value += section_adjust[internal_sym.sc]; break; @@ -1253,7 +1289,7 @@ ecoff_write_symhdr (abfd, debug, swap, where) file_ptr where; { HDRR * const symhdr = &debug->symbolic_header; - char *buff; + char *buff = NULL; ecoff_align_debug (abfd, debug, swap); @@ -1288,13 +1324,25 @@ ecoff_write_symhdr (abfd, debug, swap, where) SET (cbExtOffset, iextMax, swap->external_ext_size); #undef SET - buff = (PTR) alloca (swap->external_hdr_size); + buff = (PTR) malloc (swap->external_hdr_size); + if (buff == NULL && swap->external_hdr_size != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + (*swap->swap_hdr_out) (abfd, symhdr, buff); if (bfd_write (buff, 1, swap->external_hdr_size, abfd) != swap->external_hdr_size) - return false; + goto error_return; + if (buff != NULL) + free (buff); return true; + error_return: + if (buff != NULL) + free (buff); + return false; } /* Write out the ECOFF debugging information. This function assumes @@ -1377,10 +1425,20 @@ ecoff_write_shuffle (abfd, swap, shuffle, space) bfd_byte *s; i = swap->debug_align - (total & (swap->debug_align - 1)); - s = (bfd_byte *) alloca (i); + s = (bfd_byte *) malloc (i); + if (s == NULL && i != 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + memset ((PTR) s, 0, i); if (bfd_write ((PTR) s, 1, i, abfd) != i) - return false; + { + free (s); + return false; + } + free (s); } return true; @@ -1399,19 +1457,24 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where) file_ptr where; { struct accumulate *ainfo = (struct accumulate *) handle; - PTR space; + PTR space = NULL; if (! ecoff_write_symhdr (abfd, debug, swap, where)) - return false; + goto error_return; - space = (PTR) alloca (ainfo->largest_file_shuffle); + space = (PTR) malloc (ainfo->largest_file_shuffle); + if (space == NULL && ainfo->largest_file_shuffle != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } if (! ecoff_write_shuffle (abfd, swap, ainfo->line, space) || ! ecoff_write_shuffle (abfd, swap, ainfo->pdr, space) || ! ecoff_write_shuffle (abfd, swap, ainfo->sym, space) || ! ecoff_write_shuffle (abfd, swap, ainfo->opt, space) || ! ecoff_write_shuffle (abfd, swap, ainfo->aux, space)) - return false; + goto error_return; /* The string table is written out from the hash table if this is a final link. */ @@ -1419,7 +1482,7 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where) { BFD_ASSERT (ainfo->ss_hash == (struct string_hash_entry *) NULL); if (! ecoff_write_shuffle (abfd, swap, ainfo->ss, space)) - return false; + goto error_return; } else { @@ -1430,7 +1493,7 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where) BFD_ASSERT (ainfo->ss == (struct shuffle *) NULL); null = 0; if (bfd_write ((PTR) &null, 1, 1, abfd) != 1) - return false; + goto error_return; total = 1; BFD_ASSERT (ainfo->ss_hash == NULL || ainfo->ss_hash->val == 1); for (sh = ainfo->ss_hash; @@ -1441,7 +1504,7 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where) len = strlen (sh->root.string); if (bfd_write ((PTR) sh->root.string, 1, len + 1, abfd) != len + 1) - return false; + goto error_return; total += len + 1; } @@ -1451,10 +1514,19 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where) bfd_byte *s; i = swap->debug_align - (total & (swap->debug_align - 1)); - s = (bfd_byte *) alloca (i); + s = (bfd_byte *) malloc (i); + if (s == NULL && i != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } memset ((PTR) s, 0, i); if (bfd_write ((PTR) s, 1, i, abfd) != i) - return false; + { + free (s); + goto error_return; + } + free (s); } } @@ -1462,7 +1534,7 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where) shuffles. FIXME: They probably should be. */ if (bfd_write (debug->ssext, 1, debug->symbolic_header.issExtMax, abfd) != debug->symbolic_header.issExtMax) - return false; + goto error_return; if ((debug->symbolic_header.issExtMax & (swap->debug_align - 1)) != 0) { int i; @@ -1470,15 +1542,24 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where) i = (swap->debug_align - (debug->symbolic_header.issExtMax & (swap->debug_align - 1))); - s = (bfd_byte *) alloca (i); + s = (bfd_byte *) malloc (i); + if (s == NULL && i != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } memset ((PTR) s, 0, i); if (bfd_write ((PTR) s, 1, i, abfd) != i) - return false; + { + free (s); + goto error_return; + } + free (s); } if (! ecoff_write_shuffle (abfd, swap, ainfo->fdr, space) || ! ecoff_write_shuffle (abfd, swap, ainfo->rfd, space)) - return false; + goto error_return; BFD_ASSERT (debug->symbolic_header.cbExtOffset == 0 || debug->symbolic_header.cbExtOffset == bfd_tell (abfd)); @@ -1486,7 +1567,14 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where) if (bfd_write (debug->external_ext, swap->external_ext_size, debug->symbolic_header.iextMax, abfd) != debug->symbolic_header.iextMax * swap->external_ext_size) - return false; + goto error_return; + if (space != NULL) + free (space); return true; + + error_return: + if (space != NULL) + free (space); + return false; } diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c index 57734bc..15debf8 100644 --- a/bfd/elf32-mips.c +++ b/bfd/elf32-mips.c @@ -1028,16 +1028,21 @@ mips_elf_read_ecoff_info (abfd, section, debug) { HDRR *symhdr; const struct ecoff_debug_swap *swap; - char *ext_hdr; + char *ext_hdr = NULL; swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap; - ext_hdr = (char *) alloca (swap->external_hdr_size); + ext_hdr = (char *) malloc (swap->external_hdr_size); + if (ext_hdr == NULL && swap->external_hdr_size != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } if (bfd_get_section_contents (abfd, section, ext_hdr, (file_ptr) 0, swap->external_hdr_size) == false) - return false; + goto error_return; symhdr = &debug->symbolic_header; (*swap->swap_hdr_in) (abfd, ext_hdr, symhdr); @@ -1052,13 +1057,13 @@ mips_elf_read_ecoff_info (abfd, section, debug) debug->ptr = (type) malloc (size * symhdr->count); \ if (debug->ptr == NULL) \ { \ - bfd_error = no_memory; \ - return false; \ + bfd_set_error (bfd_error_no_memory); \ + goto error_return; \ } \ if (bfd_seek (abfd, (file_ptr) symhdr->offset, SEEK_SET) != 0 \ || (bfd_read (debug->ptr, size, symhdr->count, \ abfd) != size * symhdr->count)) \ - return false; \ + goto error_return; \ } READ (external_ext, cbExtOffset, iextMax, swap->external_ext_size, PTR); @@ -1075,8 +1080,36 @@ mips_elf_read_ecoff_info (abfd, section, debug) READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, PTR); debug->fdr = NULL; + debug->adjust = NULL; return true; + + error_return: + if (ext_hdr != NULL) + free (ext_hdr); + if (debug->external_ext != NULL) + free (debug->external_ext); + if (debug->line != NULL) + free (debug->line); + if (debug->external_dnr != NULL) + free (debug->external_dnr); + if (debug->external_pdr != NULL) + free (debug->external_pdr); + if (debug->external_sym != NULL) + free (debug->external_sym); + if (debug->external_opt != NULL) + free (debug->external_opt); + if (debug->external_aux != NULL) + free (debug->external_aux); + if (debug->ss != NULL) + free (debug->ss); + if (debug->ssext != NULL) + free (debug->ssext); + if (debug->external_fdr != NULL) + free (debug->external_fdr); + if (debug->external_rfd != NULL) + free (debug->external_rfd); + return false; } /* Get EXTR information for a symbol. */ @@ -1301,10 +1334,6 @@ mips_elf_final_link (abfd, info) if (p->type != bfd_indirect_link_order) continue; -#ifndef alloca - alloca (0); -#endif - input_section = p->u.indirect.section; input_bfd = input_section->owner; @@ -1433,7 +1462,10 @@ mips_elf_final_link (abfd, info) p != (struct bfd_link_order *) NULL; p = p->next) { - if (p->type == bfd_indirect_link_order) + if (p->type == bfd_section_reloc_link_order + || p->type == bfd_symbol_reloc_link_order) + ++o->reloc_count; + else if (p->type == bfd_indirect_link_order) { asection *input_section; bfd *input_bfd; @@ -1446,9 +1478,9 @@ mips_elf_final_link (abfd, info) relsize = bfd_get_reloc_upper_bound (input_bfd, input_section); relocs = (arelent **) malloc (relsize); - if (!relocs) + if (!relocs && relsize != 0) { - bfd_error = no_memory; + bfd_set_error (bfd_error_no_memory); return false; } reloc_count = @@ -1468,9 +1500,10 @@ mips_elf_final_link (abfd, info) * sizeof (arelent *)))); if (!o->orelocation) { - bfd_error = no_memory; + bfd_set_error (bfd_error_no_memory); return false; } + o->flags |= SEC_RELOC; /* Reset the count so that it can be used as an index when putting in the output relocs. */ o->reloc_count = 0; @@ -1515,8 +1548,18 @@ mips_elf_final_link (abfd, info) p != (struct bfd_link_order *) NULL; p = p->next) { - if (! _bfd_default_link_order (abfd, info, o, p)) - return false; + switch (p->type) + { + case bfd_section_reloc_link_order: + case bfd_symbol_reloc_link_order: + if (! _bfd_generic_reloc_link_order (abfd, info, o, p)) + return false; + break; + default: + if (! _bfd_default_link_order (abfd, info, o, p)) + return false; + break; + } } } @@ -1661,6 +1704,7 @@ static const struct ecoff_debug_swap mips_elf_ecoff_debug_swap = mips_elf_final_write_processing #define elf_backend_ecoff_debug_swap &mips_elf_ecoff_debug_swap +#define bfd_elf32_bfd_link_add_symbols _bfd_generic_link_add_symbols_collect #define bfd_elf32_bfd_final_link mips_elf_final_link #include "elf32-target.h" diff --git a/bfd/libecoff.h b/bfd/libecoff.h index 74361c5..f0f3a11 100644 --- a/bfd/libecoff.h +++ b/bfd/libecoff.h @@ -18,65 +18,56 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "bfdlink.h" + +#ifndef ECOFF_H +#include "coff/ecoff.h" +#endif + /* This is the backend information kept for ECOFF files. This - structure is constant for a particular backend. ECOFF already - keeps a bfd_coff_backend_data structure in the bfd_target - backend_data field, so a pointer to this backend structure is kept - in the tdata field. */ + structure is constant for a particular backend. The first element + is the COFF backend data structure, so that ECOFF targets can use + the generic COFF code. */ -#define ecoff_backend(abfd) (ecoff_data (abfd)->backend_data) +#define ecoff_backend(abfd) \ + ((struct ecoff_backend_data *) (abfd)->xvec->backend_data) struct ecoff_backend_data { + /* COFF backend information. This must be the first field. */ + bfd_coff_backend_data coff; /* Supported architecture. */ enum bfd_architecture arch; - /* Big endian magic number. */ - int big_magic; - /* Little endian magic number. */ - int little_magic; - /* Alignment of debugging information. E.g., 4. */ - bfd_size_type debug_align; + /* Initial portion of armap string. */ + const char *armap_start; /* The page boundary used to align sections in a demand-paged executable file. E.g., 0x1000. */ bfd_vma round; + /* True if the .rdata section is part of the text segment, as on the + Alpha. False if .rdata is part of the data segment, as on the + MIPS. */ + boolean rdata_in_text; /* Bitsize of constructor entries. */ unsigned int constructor_bitsize; - /* Sizes of external symbolic information. */ - bfd_size_type external_hdr_size; - bfd_size_type external_dnr_size; - bfd_size_type external_pdr_size; - bfd_size_type external_sym_size; - bfd_size_type external_opt_size; - bfd_size_type external_fdr_size; - bfd_size_type external_rfd_size; - bfd_size_type external_ext_size; - /* Functions to swap in external symbolic data. */ - void (*swap_hdr_in) PARAMS ((bfd *, PTR, HDRR *)); - void (*swap_dnr_in) PARAMS ((bfd *, PTR, DNR *)); - void (*swap_pdr_in) PARAMS ((bfd *, PTR, PDR *)); - void (*swap_sym_in) PARAMS ((bfd *, PTR, SYMR *)); - void (*swap_opt_in) PARAMS ((bfd *, PTR, OPTR *)); - void (*swap_fdr_in) PARAMS ((bfd *, PTR, FDR *)); - void (*swap_rfd_in) PARAMS ((bfd *, PTR, RFDT *)); - void (*swap_ext_in) PARAMS ((bfd *, PTR, EXTR *)); - /* Functions to swap out external symbolic data. */ - void (*swap_hdr_out) PARAMS ((bfd *, const HDRR *, PTR)); - void (*swap_dnr_out) PARAMS ((bfd *, const DNR *, PTR)); - void (*swap_pdr_out) PARAMS ((bfd *, const PDR *, PTR)); - void (*swap_sym_out) PARAMS ((bfd *, const SYMR *, PTR)); - void (*swap_opt_out) PARAMS ((bfd *, const OPTR *, PTR)); - void (*swap_fdr_out) PARAMS ((bfd *, const FDR *, PTR)); - void (*swap_rfd_out) PARAMS ((bfd *, const RFDT *, PTR)); - void (*swap_ext_out) PARAMS ((bfd *, const EXTR *, PTR)); - /* It so happens that the auxiliary type information has the same - type and format for all known ECOFF targets. I don't see any - reason that that should change, so at least for now the auxiliary - swapping information is not in this table. */ + /* Reloc to use for constructor entries. */ + CONST struct reloc_howto_struct *constructor_reloc; + /* How to swap debugging information. */ + struct ecoff_debug_swap debug_swap; /* External reloc size. */ bfd_size_type external_reloc_size; /* Reloc swapping functions. */ void (*swap_reloc_in) PARAMS ((bfd *, PTR, struct internal_reloc *)); void (*swap_reloc_out) PARAMS ((bfd *, const struct internal_reloc *, PTR)); + /* Backend reloc tweaking. */ + void (*adjust_reloc_in) PARAMS ((bfd *, const struct internal_reloc *, + arelent *)); + void (*adjust_reloc_out) PARAMS ((bfd *, const arelent *, + struct internal_reloc *)); + /* Relocate section contents while linking. */ + boolean (*relocate_section) PARAMS ((bfd *output_bfd, struct bfd_link_info *, + bfd *input_bfd, asection *input_section, + bfd_byte *contents, + PTR external_relocs)); }; /* This is the target specific information kept for ECOFF files. */ @@ -85,9 +76,6 @@ struct ecoff_backend_data typedef struct ecoff_tdata { - /* Constant backend information. */ - const struct ecoff_backend_data *backend_data; - /* The reloc file position, set by ecoff_compute_section_file_positions. */ file_ptr reloc_filepos; @@ -109,42 +97,30 @@ typedef struct ecoff_tdata int gp_size; /* The register masks. When linking, all the masks found in the - input files are combined into the masks of the output file. */ + input files are combined into the masks of the output file. + These are not all used for all targets, but that's OK, because + the relevant ones are the only ones swapped in and out. */ unsigned long gprmask; + unsigned long fprmask; unsigned long cprmask[4]; - /* The size of the unswapped ECOFF symbolic information. */ - bfd_size_type raw_size; + /* The ECOFF symbolic debugging information. */ + struct ecoff_debug_info debug_info; /* The unswapped ECOFF symbolic information. */ PTR raw_syments; - /* The swapped ECOFF symbolic header. */ - HDRR symbolic_header; - - /* Pointers to the unswapped symbolic information. */ - unsigned char *line; - PTR external_dnr; /* struct dnr_ext */ - PTR external_pdr; /* struct pdr_ext */ - PTR external_sym; /* struct sym_ext */ - PTR external_opt; /* struct opt_ext */ - union aux_ext *external_aux; - char *ss; - char *ssext; - PTR external_fdr; /* struct fdr_ext */ - PTR external_rfd; /* struct rfd_ext */ - PTR external_ext; /* struct ext_ext */ - - /* The swapped FDR information. */ - FDR *fdr; - - /* The FDR index. This is set for an input BFD to a link so that - the external symbols can set their FDR index correctly. */ - unsigned int ifdbase; - /* The canonical BFD symbols. */ struct ecoff_symbol_struct *canonical_symbols; + /* A mapping from external symbol numbers to entries in the linker + hash table, used when linking. */ + struct ecoff_link_hash_entry **sym_hashes; + + /* A mapping from reloc symbol indices to sections, used when + linking. */ + asection **symndx_to_section; + } ecoff_data_type; /* Each canonical asymbol really looks like this. */ @@ -176,13 +152,71 @@ typedef struct ecoff_symbol_struct #define ecoff_get_sym_index(symbol) ((unsigned long) (symbol)->udata) #define ecoff_set_sym_index(symbol, idx) ((symbol)->udata = (PTR) (idx)) -/* Make an empty ECOFF symbol. */ -extern asymbol *ecoff_make_empty_symbol PARAMS ((bfd *abfd)); +/* When generating MIPS embedded PIC code, the linker relaxes the code + to turn PC relative branches into longer code sequences when the PC + relative branch is out of range. This involves reading the relocs + in bfd_relax_section as well as in bfd_final_link, and requires the + code to keep track of which relocs have been expanded. A pointer + to this structure is put in the used_by_bfd pointer of a section to + keep track of this information. The user_by_bfd pointer will be + NULL if the information was not needed. */ + +struct ecoff_section_tdata +{ + /* The unswapped relocs for this section. These are stored in + memory so the input file does not have to be read twice. */ + PTR external_relocs; + + /* The contents of the section. These bytes may or may not be saved + in memory, but if it is this is a pointer to them. */ + bfd_byte *contents; + + /* Offset adjustments for PC relative branches. A number other than + 1 is an addend for a PC relative branch; this addend arises because + it crosses one or more branches which were expanded into a larger + code sequence. A 1 means that this branch was itself expanded into + a larger code sequence. 1 is not a possible offset, since all + offsets must be multiples of the instruction size, which is 4; + also, the only relocs with non-zero offsets will be PC relative + branches within the same object file. If this field is NULL, no + branches were expanded and no offsets are required. Otherwise + there are as many entries as there are relocs in the section, and + the entry for any reloc that is not PC relative is zero. */ + long *offsets; +}; + +/* An accessor macro for the ecoff_section_tdata structure. */ +#define ecoff_section_data(abfd, sec) \ + ((struct ecoff_section_tdata *) (sec)->used_by_bfd) + +/* ECOFF linker hash table entries. */ + +struct ecoff_link_hash_entry +{ + struct bfd_link_hash_entry root; + /* Symbol index in output file. */ + long indx; + /* BFD that ext field value came from. */ + bfd *abfd; + /* ECOFF external symbol information. */ + EXTR esym; +}; + +/* ECOFF linker hash table. */ + +struct ecoff_link_hash_table +{ + struct bfd_link_hash_table root; +}; + +/* Make an ECOFF object. */ +extern boolean ecoff_mkobject PARAMS ((bfd *)); /* Read in the ECOFF symbolic debugging information. */ extern boolean ecoff_slurp_symbolic_info PARAMS ((bfd *)); /* Generic ECOFF BFD backend vectors. */ +extern asymbol *ecoff_make_empty_symbol PARAMS ((bfd *abfd)); extern unsigned int ecoff_get_symtab_upper_bound PARAMS ((bfd *abfd)); extern unsigned int ecoff_get_symtab PARAMS ((bfd *abfd, asymbol **alocation)); @@ -196,8 +230,6 @@ extern unsigned int ecoff_canonicalize_reloc PARAMS ((bfd *abfd, asection *section, arelent **relptr, asymbol **symbols)); -extern CONST struct reloc_howto_struct *ecoff_bfd_reloc_type_lookup - PARAMS ((bfd *abfd, bfd_reloc_code_real_type code)); extern boolean ecoff_find_nearest_line PARAMS ((bfd *abfd, asection *section, asymbol **symbols, @@ -205,8 +237,6 @@ extern boolean ecoff_find_nearest_line PARAMS ((bfd *abfd, CONST char **filename_ptr, CONST char **fnname_ptr, unsigned int *retline_ptr)); -extern boolean ecoff_bfd_seclet_link PARAMS ((bfd *abfd, PTR data, - boolean relocateable)); extern boolean ecoff_set_arch_mach PARAMS ((bfd *abfd, enum bfd_architecture arch, unsigned long machine)); @@ -216,6 +246,11 @@ extern boolean ecoff_set_section_contents PARAMS ((bfd *abfd, PTR location, file_ptr offset, bfd_size_type count)); +extern boolean ecoff_get_section_contents PARAMS ((bfd *abfd, + asection *section, + PTR location, + file_ptr offset, + bfd_size_type count)); extern boolean ecoff_write_object_contents PARAMS ((bfd *abfd)); extern boolean ecoff_slurp_armap PARAMS ((bfd *abfd)); extern boolean ecoff_write_armap PARAMS ((bfd *abfd, unsigned int elength, @@ -229,20 +264,34 @@ extern bfd_target *ecoff_archive_p PARAMS ((bfd *abfd)); #define ecoff_truncate_arname bfd_dont_truncate_arname #define ecoff_openr_next_archived_file bfd_generic_openr_next_archived_file #define ecoff_generic_stat_arch_elt bfd_generic_stat_arch_elt -#define ecoff_get_section_contents bfd_generic_get_section_contents #define ecoff_get_reloc_upper_bound coff_get_reloc_upper_bound #define ecoff_close_and_cleanup bfd_generic_close_and_cleanup #define ecoff_bfd_debug_info_start bfd_void #define ecoff_bfd_debug_info_end bfd_void #define ecoff_bfd_debug_info_accumulate \ ((void (*) PARAMS ((bfd *, struct sec *))) bfd_void) -#define ecoff_bfd_get_relocated_section_contents \ - bfd_generic_get_relocated_section_contents -#define ecoff_bfd_relax_section bfd_generic_relax_section #define ecoff_bfd_make_debug_symbol \ ((asymbol *(*) PARAMS ((bfd *, void *, unsigned long))) bfd_nullvoidptr) +extern struct bfd_link_hash_table *ecoff_bfd_link_hash_table_create + PARAMS ((bfd *)); +extern boolean ecoff_bfd_link_add_symbols + PARAMS ((bfd *, struct bfd_link_info *)); +extern boolean ecoff_bfd_final_link PARAMS ((bfd *, struct bfd_link_info *)); + +#ifndef ecoff_bfd_copy_private_section_data +#define ecoff_bfd_copy_private_section_data \ + ((boolean (*) PARAMS ((bfd *, asection *, bfd *, asection *))) bfd_true) +#endif +#ifndef ecoff_bfd_copy_private_bfd_data +#define ecoff_bfd_copy_private_bfd_data \ + ((boolean (*) PARAMS ((bfd *, bfd *))) bfd_true) +#endif +#ifndef ecoff_bfd_is_local_label +#define ecoff_bfd_is_local_label bfd_generic_is_local_label +#endif /* Hook functions for the generic COFF section reading code. */ +extern PTR ecoff_mkobject_hook PARAMS ((bfd *, PTR filehdr, PTR aouthdr)); extern asection *ecoff_make_section_hook PARAMS ((bfd *abfd, char *name)); extern boolean ecoff_new_section_hook PARAMS ((bfd *abfd, asection *section)); -- 2.7.4