/* Routines to link ECOFF debugging information.
- Copyright 1993 Free Software Foundation, Inc.
+ Copyright 1993, 94, 95, 96, 97, 1999 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support, <ian@cygnus.com>.
This file is part of BFD, the Binary File Descriptor library.
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. */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "bfd.h"
#include "sysdep.h"
#include "bfdlink.h"
#include "libbfd.h"
-#include "obstack.h"
+#include "objalloc.h"
+#include "aout/stab_gnu.h"
#include "coff/internal.h"
#include "coff/sym.h"
#include "coff/symconst.h"
#include "coff/ecoff.h"
+#include "libcoff.h"
+#include "libecoff.h"
\f
static boolean ecoff_add_bytes PARAMS ((char **buf, char **bufend,
size_t need));
static boolean ecoff_write_symhdr PARAMS ((bfd *, struct ecoff_debug_info *,
const struct ecoff_debug_swap *,
file_ptr where));
+static int cmp_fdrtab_entry PARAMS ((const PTR, const PTR));
+static boolean mk_fdrtab PARAMS ((bfd *,
+ struct ecoff_debug_info * const,
+ const struct ecoff_debug_swap * const,
+ struct ecoff_find_line *));
+static long fdrtab_lookup PARAMS ((struct ecoff_find_line *, bfd_vma));
+static boolean lookup_line
+ PARAMS ((bfd *, struct ecoff_debug_info * const,
+ const struct ecoff_debug_swap * const, struct ecoff_find_line *));
+\f
+/* Routines to swap auxiliary information in and out. I am assuming
+ that the auxiliary information format is always going to be target
+ independent. */
+
+/* Swap in a type information record.
+ BIGEND says whether AUX symbols are big-endian or little-endian; this
+ info comes from the file header record (fh-fBigendian). */
+
+void
+_bfd_ecoff_swap_tir_in (bigend, ext_copy, intern)
+ int bigend;
+ const struct tir_ext *ext_copy;
+ TIR *intern;
+{
+ struct tir_ext ext[1];
+
+ *ext = *ext_copy; /* Make it reasonable to do in-place. */
+
+ /* now the fun stuff... */
+ if (bigend) {
+ intern->fBitfield = 0 != (ext->t_bits1[0] & TIR_BITS1_FBITFIELD_BIG);
+ intern->continued = 0 != (ext->t_bits1[0] & TIR_BITS1_CONTINUED_BIG);
+ intern->bt = (ext->t_bits1[0] & TIR_BITS1_BT_BIG)
+ >> TIR_BITS1_BT_SH_BIG;
+ intern->tq4 = (ext->t_tq45[0] & TIR_BITS_TQ4_BIG)
+ >> TIR_BITS_TQ4_SH_BIG;
+ intern->tq5 = (ext->t_tq45[0] & TIR_BITS_TQ5_BIG)
+ >> TIR_BITS_TQ5_SH_BIG;
+ intern->tq0 = (ext->t_tq01[0] & TIR_BITS_TQ0_BIG)
+ >> TIR_BITS_TQ0_SH_BIG;
+ intern->tq1 = (ext->t_tq01[0] & TIR_BITS_TQ1_BIG)
+ >> TIR_BITS_TQ1_SH_BIG;
+ intern->tq2 = (ext->t_tq23[0] & TIR_BITS_TQ2_BIG)
+ >> TIR_BITS_TQ2_SH_BIG;
+ intern->tq3 = (ext->t_tq23[0] & TIR_BITS_TQ3_BIG)
+ >> TIR_BITS_TQ3_SH_BIG;
+ } else {
+ intern->fBitfield = 0 != (ext->t_bits1[0] & TIR_BITS1_FBITFIELD_LITTLE);
+ intern->continued = 0 != (ext->t_bits1[0] & TIR_BITS1_CONTINUED_LITTLE);
+ intern->bt = (ext->t_bits1[0] & TIR_BITS1_BT_LITTLE)
+ >> TIR_BITS1_BT_SH_LITTLE;
+ intern->tq4 = (ext->t_tq45[0] & TIR_BITS_TQ4_LITTLE)
+ >> TIR_BITS_TQ4_SH_LITTLE;
+ intern->tq5 = (ext->t_tq45[0] & TIR_BITS_TQ5_LITTLE)
+ >> TIR_BITS_TQ5_SH_LITTLE;
+ intern->tq0 = (ext->t_tq01[0] & TIR_BITS_TQ0_LITTLE)
+ >> TIR_BITS_TQ0_SH_LITTLE;
+ intern->tq1 = (ext->t_tq01[0] & TIR_BITS_TQ1_LITTLE)
+ >> TIR_BITS_TQ1_SH_LITTLE;
+ intern->tq2 = (ext->t_tq23[0] & TIR_BITS_TQ2_LITTLE)
+ >> TIR_BITS_TQ2_SH_LITTLE;
+ intern->tq3 = (ext->t_tq23[0] & TIR_BITS_TQ3_LITTLE)
+ >> TIR_BITS_TQ3_SH_LITTLE;
+ }
+
+#ifdef TEST
+ if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+ abort();
+#endif
+}
+
+/* Swap out a type information record.
+ BIGEND says whether AUX symbols are big-endian or little-endian; this
+ info comes from the file header record (fh-fBigendian). */
+
+void
+_bfd_ecoff_swap_tir_out (bigend, intern_copy, ext)
+ int bigend;
+ const TIR *intern_copy;
+ struct tir_ext *ext;
+{
+ TIR intern[1];
+
+ *intern = *intern_copy; /* Make it reasonable to do in-place. */
+
+ /* now the fun stuff... */
+ if (bigend) {
+ ext->t_bits1[0] = ((intern->fBitfield ? TIR_BITS1_FBITFIELD_BIG : 0)
+ | (intern->continued ? TIR_BITS1_CONTINUED_BIG : 0)
+ | ((intern->bt << TIR_BITS1_BT_SH_BIG)
+ & TIR_BITS1_BT_BIG));
+ ext->t_tq45[0] = (((intern->tq4 << TIR_BITS_TQ4_SH_BIG)
+ & TIR_BITS_TQ4_BIG)
+ | ((intern->tq5 << TIR_BITS_TQ5_SH_BIG)
+ & TIR_BITS_TQ5_BIG));
+ ext->t_tq01[0] = (((intern->tq0 << TIR_BITS_TQ0_SH_BIG)
+ & TIR_BITS_TQ0_BIG)
+ | ((intern->tq1 << TIR_BITS_TQ1_SH_BIG)
+ & TIR_BITS_TQ1_BIG));
+ ext->t_tq23[0] = (((intern->tq2 << TIR_BITS_TQ2_SH_BIG)
+ & TIR_BITS_TQ2_BIG)
+ | ((intern->tq3 << TIR_BITS_TQ3_SH_BIG)
+ & TIR_BITS_TQ3_BIG));
+ } else {
+ ext->t_bits1[0] = ((intern->fBitfield ? TIR_BITS1_FBITFIELD_LITTLE : 0)
+ | (intern->continued ? TIR_BITS1_CONTINUED_LITTLE : 0)
+ | ((intern->bt << TIR_BITS1_BT_SH_LITTLE)
+ & TIR_BITS1_BT_LITTLE));
+ ext->t_tq45[0] = (((intern->tq4 << TIR_BITS_TQ4_SH_LITTLE)
+ & TIR_BITS_TQ4_LITTLE)
+ | ((intern->tq5 << TIR_BITS_TQ5_SH_LITTLE)
+ & TIR_BITS_TQ5_LITTLE));
+ ext->t_tq01[0] = (((intern->tq0 << TIR_BITS_TQ0_SH_LITTLE)
+ & TIR_BITS_TQ0_LITTLE)
+ | ((intern->tq1 << TIR_BITS_TQ1_SH_LITTLE)
+ & TIR_BITS_TQ1_LITTLE));
+ ext->t_tq23[0] = (((intern->tq2 << TIR_BITS_TQ2_SH_LITTLE)
+ & TIR_BITS_TQ2_LITTLE)
+ | ((intern->tq3 << TIR_BITS_TQ3_SH_LITTLE)
+ & TIR_BITS_TQ3_LITTLE));
+ }
+
+#ifdef TEST
+ if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+ abort();
+#endif
+}
+
+/* Swap in a relative symbol record. BIGEND says whether it is in
+ big-endian or little-endian format.*/
+
+void
+_bfd_ecoff_swap_rndx_in (bigend, ext_copy, intern)
+ int bigend;
+ const struct rndx_ext *ext_copy;
+ RNDXR *intern;
+{
+ struct rndx_ext ext[1];
-/* Obstack allocation and deallocation routines. */
-#define obstack_chunk_alloc malloc
-#define obstack_chunk_free free
+ *ext = *ext_copy; /* Make it reasonable to do in-place. */
+
+ /* now the fun stuff... */
+ if (bigend) {
+ intern->rfd = (ext->r_bits[0] << RNDX_BITS0_RFD_SH_LEFT_BIG)
+ | ((ext->r_bits[1] & RNDX_BITS1_RFD_BIG)
+ >> RNDX_BITS1_RFD_SH_BIG);
+ intern->index = ((ext->r_bits[1] & RNDX_BITS1_INDEX_BIG)
+ << RNDX_BITS1_INDEX_SH_LEFT_BIG)
+ | (ext->r_bits[2] << RNDX_BITS2_INDEX_SH_LEFT_BIG)
+ | (ext->r_bits[3] << RNDX_BITS3_INDEX_SH_LEFT_BIG);
+ } else {
+ intern->rfd = (ext->r_bits[0] << RNDX_BITS0_RFD_SH_LEFT_LITTLE)
+ | ((ext->r_bits[1] & RNDX_BITS1_RFD_LITTLE)
+ << RNDX_BITS1_RFD_SH_LEFT_LITTLE);
+ intern->index = ((ext->r_bits[1] & RNDX_BITS1_INDEX_LITTLE)
+ >> RNDX_BITS1_INDEX_SH_LITTLE)
+ | (ext->r_bits[2] << RNDX_BITS2_INDEX_SH_LEFT_LITTLE)
+ | ((unsigned int) ext->r_bits[3]
+ << RNDX_BITS3_INDEX_SH_LEFT_LITTLE);
+ }
+
+#ifdef TEST
+ if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+ abort();
+#endif
+}
+
+/* Swap out a relative symbol record. BIGEND says whether it is in
+ big-endian or little-endian format.*/
+
+void
+_bfd_ecoff_swap_rndx_out (bigend, intern_copy, ext)
+ int bigend;
+ const RNDXR *intern_copy;
+ struct rndx_ext *ext;
+{
+ RNDXR intern[1];
+
+ *intern = *intern_copy; /* Make it reasonable to do in-place. */
+
+ /* now the fun stuff... */
+ if (bigend) {
+ ext->r_bits[0] = intern->rfd >> RNDX_BITS0_RFD_SH_LEFT_BIG;
+ ext->r_bits[1] = (((intern->rfd << RNDX_BITS1_RFD_SH_BIG)
+ & RNDX_BITS1_RFD_BIG)
+ | ((intern->index >> RNDX_BITS1_INDEX_SH_LEFT_BIG)
+ & RNDX_BITS1_INDEX_BIG));
+ ext->r_bits[2] = intern->index >> RNDX_BITS2_INDEX_SH_LEFT_BIG;
+ ext->r_bits[3] = intern->index >> RNDX_BITS3_INDEX_SH_LEFT_BIG;
+ } else {
+ ext->r_bits[0] = intern->rfd >> RNDX_BITS0_RFD_SH_LEFT_LITTLE;
+ ext->r_bits[1] = (((intern->rfd >> RNDX_BITS1_RFD_SH_LEFT_LITTLE)
+ & RNDX_BITS1_RFD_LITTLE)
+ | ((intern->index << RNDX_BITS1_INDEX_SH_LITTLE)
+ & RNDX_BITS1_INDEX_LITTLE));
+ ext->r_bits[2] = intern->index >> RNDX_BITS2_INDEX_SH_LEFT_LITTLE;
+ ext->r_bits[3] = intern->index >> RNDX_BITS3_INDEX_SH_LEFT_LITTLE;
+ }
+
+#ifdef TEST
+ if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+ abort();
+#endif
+}
\f
/* The minimum amount of data to allocate. */
#define ALLOC_SIZE (4064)
if (want < ALLOC_SIZE)
want = ALLOC_SIZE;
}
- if (*buf == NULL)
- newbuf = (char *) malloc (have + want);
- else
- newbuf = (char *) realloc (*buf, have + want);
+ newbuf = (char *) bfd_realloc (*buf, have + want);
if (newbuf == NULL)
- {
- bfd_error = no_memory;
- return false;
- }
+ return false;
*buf = newbuf;
*bufend = *buf + have + want;
return true;
ret = ((struct string_hash_entry *)
bfd_hash_allocate (table, sizeof (struct string_hash_entry)));
if (ret == (struct string_hash_entry *) NULL)
- {
- bfd_error = no_memory;
- return NULL;
- }
+ return NULL;
/* Call the allocation method of the superclass. */
ret = ((struct string_hash_entry *)
struct shuffle *rfd_end;
/* The size of the largest file shuffle. */
unsigned long largest_file_shuffle;
- /* An obstack for debugging information. */
- struct obstack memory;
+ /* An objalloc for debugging information. */
+ struct objalloc *memory;
};
/* Add a file entry to a shuffle list. */
if (*tail != (struct shuffle *) NULL
&& (*tail)->filep
&& (*tail)->u.file.input_bfd == input_bfd
- && (*tail)->u.file.offset + (*tail)->size == offset)
+ && (*tail)->u.file.offset + (*tail)->size == (unsigned long) offset)
{
/* Just merge this entry onto the existing one. */
(*tail)->size += size;
return true;
}
- n = (struct shuffle *) obstack_alloc (&ainfo->memory,
- sizeof (struct shuffle));
+ n = (struct shuffle *) objalloc_alloc (ainfo->memory,
+ sizeof (struct shuffle));
if (!n)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return false;
}
n->next = NULL;
{
struct shuffle *n;
- n = (struct shuffle *) obstack_alloc (&ainfo->memory,
- sizeof (struct shuffle));
+ n = (struct shuffle *) objalloc_alloc (ainfo->memory,
+ sizeof (struct shuffle));
if (!n)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return false;
}
n->next = NULL;
/*ARGSUSED*/
PTR
bfd_ecoff_debug_init (output_bfd, output_debug, output_swap, info)
- bfd *output_bfd;
+ bfd *output_bfd ATTRIBUTE_UNUSED;
struct ecoff_debug_info *output_debug;
- const struct ecoff_debug_swap *output_swap;
+ const struct ecoff_debug_swap *output_swap ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
{
struct accumulate *ainfo;
- ainfo = (struct accumulate *) malloc (sizeof (struct accumulate));
+ ainfo = (struct accumulate *) bfd_malloc (sizeof (struct accumulate));
if (!ainfo)
- {
- bfd_error = no_memory;
- return NULL;
- }
+ return NULL;
if (! bfd_hash_table_init_n (&ainfo->fdr_hash.table, string_hash_newfunc,
1021))
return NULL;
output_debug->symbolic_header.issMax = 1;
}
- if (!obstack_begin (&ainfo->memory, 4050))
+ ainfo->memory = objalloc_create ();
+ if (ainfo->memory == NULL)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return NULL;
}
void
bfd_ecoff_debug_free (handle, output_bfd, output_debug, output_swap, info)
PTR handle;
- bfd *output_bfd;
- struct ecoff_debug_info *output_debug;
- const struct ecoff_debug_swap *output_swap;
+ bfd *output_bfd ATTRIBUTE_UNUSED;
+ struct ecoff_debug_info *output_debug ATTRIBUTE_UNUSED;
+ const struct ecoff_debug_swap *output_swap ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
{
struct accumulate *ainfo = (struct accumulate *) handle;
if (! info->relocateable)
bfd_hash_table_free (&ainfo->str_hash.table);
- obstack_free (&ainfo->memory, (PTR) NULL);
+ objalloc_free (ainfo->memory);
free (ainfo);
}
SET (".rodata", scRData);
SET (".init", scInit);
SET (".fini", scFini);
+ SET (".rconst", scRConst);
#undef SET
* sizeof (RFDT)));
sz = (input_symhdr->crfd + input_symhdr->ifdMax) * external_rfd_size;
- rfd_out = (bfd_byte *) obstack_alloc (&ainfo->memory, sz);
+ rfd_out = (bfd_byte *) objalloc_alloc (ainfo->memory, sz);
if (!input_debug->ifdmap || !rfd_out)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return false;
}
if (!add_memory_shuffle (ainfo, &ainfo->rfd, &ainfo->rfd_end, rfd_out, sz))
struct string_hash_entry *fh;
/* We look up a string formed from the file name and the
- number of symbols. Sometimes an include file will
- conditionally define a typedef or something based on the
- order of include files. Using the number of symbols as a
- hash reduces the chance that we will merge symbol
- information that should not be merged. */
+ number of symbols and aux entries. Sometimes an include
+ file will conditionally define a typedef or something
+ based on the order of include files. Using the number of
+ symbols and aux entries as a 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);
- sprintf (lookup, "%s %lx", name, fdr.csym);
+
+ lookup = (char *) bfd_malloc (strlen (name) + 20);
+ if (lookup == NULL)
+ return false;
+ sprintf (lookup, "%s %lx %lx", name, fdr.csym, fdr.caux);
fh = string_hash_lookup (&ainfo->fdr_hash, lookup, true, true);
+ free (lookup);
if (fh == (struct string_hash_entry *) NULL)
return false;
/* Look through the FDR's and copy over all associated debugging
information. */
sz = copied * external_fdr_size;
- fdr_out = (bfd_byte *) obstack_alloc (&ainfo->memory, sz);
+ fdr_out = (bfd_byte *) objalloc_alloc (ainfo->memory, sz);
if (!fdr_out)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return false;
}
if (!add_memory_shuffle (ainfo, &ainfo->fdr, &ainfo->fdr_end, fdr_out, sz))
fdr_ptr += fdr_add, i++)
{
FDR fdr;
+ bfd_vma fdr_adr;
bfd_byte *sym_out;
bfd_byte *lraw_src;
bfd_byte *lraw_end;
else
(*input_swap->swap_fdr_in) (input_bfd, (PTR) fdr_ptr, &fdr);
+ fdr_adr = fdr.adr;
+
+ /* Adjust the FDR address for any changes that may have been
+ made by relaxing. */
+ if (input_debug->adjust != (struct ecoff_value_adjust *) NULL)
+ {
+ struct ecoff_value_adjust *adjust;
+
+ for (adjust = input_debug->adjust;
+ adjust != (struct ecoff_value_adjust *) NULL;
+ adjust = adjust->next)
+ if (fdr_adr >= adjust->start
+ && fdr_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. */
out again. */
fgotfilename = false;
sz = fdr.csym * external_sym_size;
- sym_out = (bfd_byte *) obstack_alloc (&ainfo->memory, sz);
+ sym_out = (bfd_byte *) objalloc_alloc (ainfo->memory, sz);
if (!sym_out)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return false;
}
- if (!add_memory_shuffle (ainfo, &ainfo->sym, &ainfo->sym_end, sym_out, sz))
+ if (!add_memory_shuffle (ainfo, &ainfo->sym, &ainfo->sym_end, sym_out,
+ sz))
return false;
lraw_src = ((bfd_byte *) input_debug->external_sym
+ fdr.isymBase * input_swap->external_sym_size);
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;
output_symhdr->isymMax += fdr.csym;
/* Copy the information that does not need swapping. */
+
+ /* FIXME: If we are relaxing, we need to adjust the line
+ numbers. Frankly, forget it. Anybody using stabs debugging
+ information will not use this line number information, and
+ stabs are adjusted correctly. */
if (fdr.cbLine > 0)
{
if (!add_file_shuffle (ainfo, &ainfo->line, &ainfo->line_end,
- input_bfd,
- input_symhdr->cbLineOffset + fdr.cbLineOffset,
- fdr.cbLine))
+ input_bfd,
+ input_symhdr->cbLineOffset + fdr.cbLineOffset,
+ fdr.cbLine))
return false;
fdr.ilineBase = output_symhdr->ilineMax;
fdr.cbLineOffset = output_symhdr->cbLine;
if (fdr.caux > 0)
{
if (!add_file_shuffle (ainfo, &ainfo->aux, &ainfo->aux_end,
- input_bfd,
- (input_symhdr->cbAuxOffset
- + fdr.iauxBase * sizeof (union aux_ext)),
- fdr.caux * sizeof (union aux_ext)))
+ input_bfd,
+ (input_symhdr->cbAuxOffset
+ + fdr.iauxBase * sizeof (union aux_ext)),
+ fdr.caux * sizeof (union aux_ext)))
return false;
fdr.iauxBase = output_symhdr->iauxMax;
output_symhdr->iauxMax += fdr.caux;
else if (fdr.cbSs > 0)
{
if (!add_file_shuffle (ainfo, &ainfo->ss, &ainfo->ss_end,
- input_bfd,
- input_symhdr->cbSsOffset + fdr.issBase,
- fdr.cbSs))
+ input_bfd,
+ input_symhdr->cbSsOffset + fdr.issBase,
+ fdr.cbSs))
return false;
fdr.issBase = output_symhdr->issMax;
output_symhdr->issMax += fdr.cbSs;
}
- if (output_bfd->xvec->header_byteorder_big_p
- == input_bfd->xvec->header_byteorder_big_p)
+ if ((output_bfd->xvec->header_byteorder
+ == input_bfd->xvec->header_byteorder)
+ && input_debug->adjust == (struct ecoff_value_adjust *) NULL)
{
- /* The two BFD's have the same endianness, so simply copying
- the information will suffice. */
+ /* The two BFD's have the same endianness, and we don't have
+ to adjust the PDR addresses, so simply copying the
+ information will suffice. */
BFD_ASSERT (external_pdr_size == input_swap->external_pdr_size);
if (fdr.cpd > 0)
{
+ fdr.ipdFirst * insz);
end = in + fdr.cpd * insz;
sz = fdr.cpd * outsz;
- out = (bfd_byte *) obstack_alloc (&ainfo->memory, sz);
+ out = (bfd_byte *) objalloc_alloc (ainfo->memory, sz);
if (!out)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return false;
}
- if (!add_memory_shuffle (ainfo, &ainfo->pdr, &ainfo->pdr_end, out, sz))
+ if (!add_memory_shuffle (ainfo, &ainfo->pdr, &ainfo->pdr_end, out,
+ sz))
return false;
for (; in < end; in += insz, out += outsz)
{
PDR pdr;
(*input_swap->swap_pdr_in) (input_bfd, (PTR) in, &pdr);
+
+ /* If we have been relaxing, we may have to adjust the
+ address. */
+ if (input_debug->adjust != (struct ecoff_value_adjust *) NULL)
+ {
+ bfd_vma adr;
+ struct ecoff_value_adjust *adjust;
+
+ adr = fdr_adr + pdr.adr;
+ for (adjust = input_debug->adjust;
+ adjust != (struct ecoff_value_adjust *) NULL;
+ adjust = adjust->next)
+ if (adr >= adjust->start
+ && adr < adjust->end)
+ pdr.adr += adjust->adjust;
+ }
+
(*output_swap->swap_pdr_out) (output_bfd, &pdr, (PTR) out);
}
+ fdr.ioptBase * insz);
end = in + fdr.copt * insz;
sz = fdr.copt * outsz;
- out = (bfd_byte *) obstack_alloc (&ainfo->memory, sz);
+ out = (bfd_byte *) objalloc_alloc (ainfo->memory, sz);
if (!out)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return false;
}
- if (!add_memory_shuffle (ainfo, &ainfo->opt, &ainfo->opt_end, out, sz))
+ if (!add_memory_shuffle (ainfo, &ainfo->opt, &ainfo->opt_end, out,
+ sz))
return false;
for (; in < end; in += insz, out += outsz)
{
sh = string_hash_lookup (&ainfo->str_hash, string, true, true);
if (sh == (struct string_hash_entry *) NULL)
- return (bfd_size_type) -1;
+ return -1;
if (sh->val == -1)
{
sh->val = symhdr->issMax;
asymbol **symbols;
asymbol **sym_ptr;
asymbol **sym_end;
+ long symsize;
+ long symcount;
PTR external_fdr;
- memset (&fdr, 0, sizeof fdr);
+ memset ((PTR) &fdr, 0, sizeof fdr);
sec = bfd_get_section_by_name (input_bfd, ".text");
if (sec != NULL)
fdr.isymBase = output_symhdr->isymMax;
/* Get the local symbols from the input BFD. */
- symbols = (asymbol **) bfd_alloc (output_bfd,
- get_symtab_upper_bound (input_bfd));
+ symsize = bfd_get_symtab_upper_bound (input_bfd);
+ if (symsize < 0)
+ return false;
+ symbols = (asymbol **) bfd_alloc (output_bfd, symsize);
if (symbols == (asymbol **) NULL)
- {
- bfd_error = no_memory;
- return false;
- }
- sym_end = symbols + bfd_canonicalize_symtab (input_bfd, symbols);
+ return false;
+ symcount = bfd_canonicalize_symtab (input_bfd, symbols);
+ if (symcount < 0)
+ return false;
+ sym_end = symbols + symcount;
/* Handle the local symbols. Any external symbols are handled
separately. */
if (((*sym_ptr)->flags & BSF_EXPORT) != 0)
continue;
- memset (&internal_sym, 0, sizeof internal_sym);
+ memset ((PTR) &internal_sym, 0, sizeof internal_sym);
internal_sym.iss = ecoff_add_string (ainfo, info, output_debug, &fdr,
(*sym_ptr)->name);
if (internal_sym.iss == -1)
return false;
if (bfd_is_com_section ((*sym_ptr)->section)
- || (*sym_ptr)->section == &bfd_und_section)
+ || bfd_is_und_section ((*sym_ptr)->section))
internal_sym.value = (*sym_ptr)->value;
else
internal_sym.value = ((*sym_ptr)->value
internal_sym.sc = scUndefined;
internal_sym.index = indexNil;
- external_sym = (PTR) obstack_alloc (&ainfo->memory,
- output_swap->external_sym_size);
+ external_sym = (PTR) objalloc_alloc (ainfo->memory,
+ output_swap->external_sym_size);
if (!external_sym)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return false;
}
(*swap_sym_out) (output_bfd, &internal_sym, external_sym);
the lang field to be langC. The fBigendian field will
indicate little endian format, but it doesn't matter because
it only applies to aux fields and there are none. */
- external_fdr = (PTR) obstack_alloc (&ainfo->memory,
- output_swap->external_fdr_size);
+ external_fdr = (PTR) objalloc_alloc (ainfo->memory,
+ output_swap->external_fdr_size);
if (!external_fdr)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return false;
}
(*output_swap->swap_fdr_out) (output_bfd, &fdr, external_fdr);
}
if (bfd_is_com_section (sym_ptr->section)
- || sym_ptr->section == &bfd_und_section)
+ || bfd_is_und_section (sym_ptr->section)
+ || sym_ptr->section->output_section == (asection *) NULL)
{
/* FIXME: gas does not keep the value of a small undefined
symbol in the symbol itself, because of relocation
namelen = strlen (name);
- if (debug->ssext_end - debug->ssext
+ if ((size_t) (debug->ssext_end - debug->ssext)
< symhdr->issExtMax + namelen + 1)
{
if (ecoff_add_bytes ((char **) &debug->ssext,
== false)
return false;
}
- if ((char *) debug->external_ext_end - (char *) debug->external_ext
+ if ((size_t) ((char *) debug->external_ext_end
+ - (char *) debug->external_ext)
< (symhdr->iextMax + 1) * external_ext_size)
{
if (ecoff_add_bytes ((char **) &debug->external_ext,
/*ARGSUSED*/
static void
ecoff_align_debug (abfd, debug, swap)
- bfd *abfd;
+ bfd *abfd ATTRIBUTE_UNUSED;
struct ecoff_debug_info *debug;
const struct ecoff_debug_swap *swap;
{
if (add != debug_align)
{
if (debug->line != (unsigned char *) NULL)
- memset (debug->line + symhdr->cbLine, 0, add);
+ memset ((PTR) (debug->line + symhdr->cbLine), 0, add);
symhdr->cbLine += add;
}
if (add != debug_align)
{
if (debug->ss != (char *) NULL)
- memset (debug->ss + symhdr->issMax, 0, add);
+ memset ((PTR) (debug->ss + symhdr->issMax), 0, add);
symhdr->issMax += add;
}
if (add != debug_align)
{
if (debug->ssext != (char *) NULL)
- memset (debug->ssext + symhdr->issExtMax, 0, add);
+ memset ((PTR) (debug->ssext + symhdr->issExtMax), 0, add);
symhdr->issExtMax += add;
}
if (add != aux_align)
{
if (debug->external_aux != (union aux_ext *) NULL)
- memset (debug->external_aux + symhdr->iauxMax, 0,
+ memset ((PTR) (debug->external_aux + symhdr->iauxMax), 0,
add * sizeof (union aux_ext));
symhdr->iauxMax += add;
}
if (add != rfd_align)
{
if (debug->external_rfd != (PTR) NULL)
- memset (((char *) debug->external_rfd
- + symhdr->crfd * swap->external_rfd_size),
- 0, add * swap->external_rfd_size);
+ memset ((PTR) ((char *) debug->external_rfd
+ + symhdr->crfd * swap->external_rfd_size),
+ 0, (size_t) (add * swap->external_rfd_size));
symhdr->crfd += add;
}
}
file_ptr where;
{
HDRR * const symhdr = &debug->symbolic_header;
- char *buff;
+ char *buff = NULL;
ecoff_align_debug (abfd, debug, swap);
where += swap->external_hdr_size;
+ symhdr->magic = swap->sym_magic;
+
/* Fill in the file offsets. */
#define SET(offset, count, size) \
if (symhdr->count == 0) \
SET (cbExtOffset, iextMax, swap->external_ext_size);
#undef SET
- buff = (PTR) alloca (swap->external_hdr_size);
+ buff = (PTR) bfd_malloc ((size_t) swap->external_hdr_size);
+ if (buff == NULL && swap->external_hdr_size != 0)
+ 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
return false;
#define WRITE(ptr, count, size, offset) \
- BFD_ASSERT (symhdr->offset == 0 || bfd_tell (abfd) == symhdr->offset); \
+ BFD_ASSERT (symhdr->offset == 0 \
+ || (bfd_vma) bfd_tell (abfd) == symhdr->offset); \
if (bfd_write ((PTR) debug->ptr, size, symhdr->count, abfd) \
!= size * symhdr->count) \
return false;
if ((total & (swap->debug_align - 1)) != 0)
{
- int i;
+ unsigned int i;
bfd_byte *s;
i = swap->debug_align - (total & (swap->debug_align - 1));
- s = (bfd_byte *) alloca (i);
- memset (s, 0, i);
- if (bfd_write ((PTR) s, 1, i, abfd) != i)
+ s = (bfd_byte *) bfd_malloc (i);
+ if (s == NULL && i != 0)
return false;
+
+ memset ((PTR) s, 0, i);
+ if (bfd_write ((PTR) s, 1, i, abfd) != i)
+ {
+ free (s);
+ return false;
+ }
+ free (s);
}
return true;
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) bfd_malloc (ainfo->largest_file_shuffle);
+ if (space == NULL && ainfo->largest_file_shuffle != 0)
+ 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. */
{
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
{
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;
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;
}
if ((total & (swap->debug_align - 1)) != 0)
{
- int i;
+ unsigned int i;
bfd_byte *s;
i = swap->debug_align - (total & (swap->debug_align - 1));
- s = (bfd_byte *) alloca (i);
- memset (s, 0, i);
+ s = (bfd_byte *) bfd_malloc (i);
+ if (s == NULL && i != 0)
+ 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);
}
}
/* The external strings and symbol are not converted over to using
shuffles. FIXME: They probably should be. */
if (bfd_write (debug->ssext, 1, debug->symbolic_header.issExtMax, abfd)
- != debug->symbolic_header.issExtMax)
- return false;
+ != (bfd_size_type) debug->symbolic_header.issExtMax)
+ goto error_return;
if ((debug->symbolic_header.issExtMax & (swap->debug_align - 1)) != 0)
{
- int i;
+ unsigned int i;
bfd_byte *s;
i = (swap->debug_align
- (debug->symbolic_header.issExtMax & (swap->debug_align - 1)));
- s = (bfd_byte *) alloca (i);
- memset (s, 0, i);
+ s = (bfd_byte *) bfd_malloc (i);
+ if (s == NULL && i != 0)
+ 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));
+ || (debug->symbolic_header.cbExtOffset
+ == (bfd_vma) bfd_tell (abfd)));
if (bfd_write (debug->external_ext, swap->external_ext_size,
debug->symbolic_header.iextMax, abfd)
!= debug->symbolic_header.iextMax * swap->external_ext_size)
+ goto error_return;
+
+ if (space != NULL)
+ free (space);
+ return true;
+
+ error_return:
+ if (space != NULL)
+ free (space);
+ return false;
+}
+\f
+/* Handle the find_nearest_line function for both ECOFF and MIPS ELF
+ files. */
+
+/* Compare FDR entries. This is called via qsort. */
+
+static int
+cmp_fdrtab_entry (leftp, rightp)
+ const PTR leftp;
+ const PTR rightp;
+{
+ const struct ecoff_fdrtab_entry *lp =
+ (const struct ecoff_fdrtab_entry *) leftp;
+ const struct ecoff_fdrtab_entry *rp =
+ (const struct ecoff_fdrtab_entry *) rightp;
+
+ if (lp->base_addr < rp->base_addr)
+ return -1;
+ if (lp->base_addr > rp->base_addr)
+ return 1;
+ return 0;
+}
+
+/* Each file descriptor (FDR) has a memory address, to simplify
+ looking up an FDR by address, we build a table covering all FDRs
+ that have a least one procedure descriptor in them. The final
+ table will be sorted by address so we can look it up via binary
+ search. */
+
+static boolean
+mk_fdrtab (abfd, debug_info, debug_swap, line_info)
+ bfd *abfd;
+ struct ecoff_debug_info * const debug_info;
+ const struct ecoff_debug_swap * const debug_swap;
+ struct ecoff_find_line *line_info;
+{
+ struct ecoff_fdrtab_entry *tab;
+ FDR *fdr_ptr;
+ FDR *fdr_start;
+ FDR *fdr_end;
+ boolean stabs;
+ long len;
+
+ fdr_start = debug_info->fdr;
+ fdr_end = fdr_start + debug_info->symbolic_header.ifdMax;
+
+ /* First, let's see how long the table needs to be: */
+ for (len = 0, fdr_ptr = fdr_start; fdr_ptr < fdr_end; fdr_ptr++)
+ {
+ if (fdr_ptr->cpd == 0) /* skip FDRs that have no PDRs */
+ continue;
+ ++len;
+ }
+
+ /* Now, create and fill in the table: */
+
+ line_info->fdrtab = ((struct ecoff_fdrtab_entry*)
+ bfd_zalloc (abfd,
+ len * sizeof (struct ecoff_fdrtab_entry)));
+ if (line_info->fdrtab == NULL)
return false;
+ line_info->fdrtab_len = len;
+
+ tab = line_info->fdrtab;
+ for (fdr_ptr = fdr_start; fdr_ptr < fdr_end; fdr_ptr++)
+ {
+ if (fdr_ptr->cpd == 0)
+ continue;
+
+ /* Check whether this file has stabs debugging information. In
+ a file with stabs debugging information, the second local
+ symbol is named @stabs. */
+ stabs = false;
+ if (fdr_ptr->csym >= 2)
+ {
+ char *sym_ptr;
+ SYMR sym;
+
+ sym_ptr = ((char *) debug_info->external_sym
+ + (fdr_ptr->isymBase + 1)*debug_swap->external_sym_size);
+ (*debug_swap->swap_sym_in) (abfd, sym_ptr, &sym);
+ if (strcmp (debug_info->ss + fdr_ptr->issBase + sym.iss,
+ STABS_SYMBOL) == 0)
+ stabs = true;
+ }
+
+ if (!stabs)
+ {
+ bfd_size_type external_pdr_size;
+ char *pdr_ptr;
+ PDR pdr;
+
+ external_pdr_size = debug_swap->external_pdr_size;
+
+ pdr_ptr = ((char *) debug_info->external_pdr
+ + fdr_ptr->ipdFirst * external_pdr_size);
+ (*debug_swap->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr);
+ /* The address of the first PDR is the offset of that
+ procedure relative to the beginning of file FDR. */
+ tab->base_addr = fdr_ptr->adr - pdr.adr;
+ }
+ else
+ {
+ /* XXX I don't know about stabs, so this is a guess
+ (davidm@cs.arizona.edu): */
+ tab->base_addr = fdr_ptr->adr;
+ }
+ tab->fdr = fdr_ptr;
+ ++tab;
+ }
+
+ /* Finally, the table is sorted in increasing memory-address order.
+ The table is mostly sorted already, but there are cases (e.g.,
+ static functions in include files), where this does not hold.
+ Use "odump -PFv" to verify... */
+ qsort ((PTR) line_info->fdrtab, len,
+ sizeof (struct ecoff_fdrtab_entry), cmp_fdrtab_entry);
+
+ return true;
+}
+
+/* Return index of first FDR that covers to OFFSET. */
+
+static long
+fdrtab_lookup (line_info, offset)
+ struct ecoff_find_line *line_info;
+ bfd_vma offset;
+{
+ long low, high, len;
+ long mid = -1;
+ struct ecoff_fdrtab_entry *tab;
+
+ len = line_info->fdrtab_len;
+ if (len == 0)
+ return -1;
+
+ tab = line_info->fdrtab;
+ for (low = 0, high = len - 1 ; low != high ;)
+ {
+ mid = (high + low) / 2;
+ if (offset >= tab[mid].base_addr && offset < tab[mid + 1].base_addr)
+ goto find_min;
+
+ if (tab[mid].base_addr > offset)
+ high = mid;
+ else
+ low = mid + 1;
+ }
+ ++mid;
+
+ /* last entry is catch-all for all higher addresses: */
+ if (offset < tab[mid].base_addr)
+ return -1;
+
+ find_min:
+
+ while (mid > 0 && tab[mid - 1].base_addr == tab[mid].base_addr)
+ --mid;
+
+ return mid;
+}
+
+/* Look up a line given an address, storing the information in
+ LINE_INFO->cache. */
+
+static boolean
+lookup_line (abfd, debug_info, debug_swap, line_info)
+ bfd *abfd;
+ struct ecoff_debug_info * const debug_info;
+ const struct ecoff_debug_swap * const debug_swap;
+ struct ecoff_find_line *line_info;
+{
+ struct ecoff_fdrtab_entry *tab;
+ bfd_vma offset;
+ boolean stabs;
+ FDR *fdr_ptr;
+ int i;
+
+ offset = line_info->cache.start;
+
+ /* Build FDR table (sorted by object file's base-address) if we
+ don't have it already. */
+ if (line_info->fdrtab == NULL
+ && !mk_fdrtab (abfd, debug_info, debug_swap, line_info))
+ return false;
+
+ tab = line_info->fdrtab;
+
+ /* find first FDR for address OFFSET */
+ i = fdrtab_lookup (line_info, offset);
+ if (i < 0)
+ return false; /* no FDR, no fun... */
+ fdr_ptr = tab[i].fdr;
+
+ /* Check whether this file has stabs debugging information. In a
+ file with stabs debugging information, the second local symbol is
+ named @stabs. */
+ stabs = false;
+ if (fdr_ptr->csym >= 2)
+ {
+ char *sym_ptr;
+ SYMR sym;
+
+ sym_ptr = ((char *) debug_info->external_sym
+ + (fdr_ptr->isymBase + 1) * debug_swap->external_sym_size);
+ (*debug_swap->swap_sym_in) (abfd, sym_ptr, &sym);
+ if (strcmp (debug_info->ss + fdr_ptr->issBase + sym.iss,
+ STABS_SYMBOL) == 0)
+ stabs = true;
+ }
+
+ if (!stabs)
+ {
+ bfd_size_type external_pdr_size;
+ char *pdr_ptr;
+ char *best_pdr = NULL;
+ FDR *best_fdr;
+ bfd_vma best_dist = ~0;
+ PDR pdr;
+ unsigned char *line_ptr;
+ unsigned char *line_end;
+ int lineno;
+ /* This file uses ECOFF debugging information. Each FDR has a
+ list of procedure descriptors (PDR). The address in the FDR
+ is the absolute address of the first procedure. The address
+ in the first PDR gives the offset of that procedure relative
+ to the object file's base-address. The addresses in
+ subsequent PDRs specify each procedure's address relative to
+ the object file's base-address. To make things more juicy,
+ whenever the PROF bit in the PDR is set, the real entry point
+ of the procedure may be 16 bytes below what would normally be
+ the procedure's entry point. Instead, DEC came up with a
+ wicked scheme to create profiled libraries "on the fly":
+ instead of shipping a regular and a profiled version of each
+ library, they insert 16 bytes of unused space in front of
+ each procedure and set the "prof" bit in the PDR to indicate
+ that there is a gap there (this is done automagically by "as"
+ when option "-pg" is specified). Thus, normally, you link
+ against such a library and, except for lots of 16 byte gaps
+ between functions, things will behave as usual. However,
+ when invoking "ld" with option "-pg", it will fill those gaps
+ with code that calls mcount(). It then moves the function's
+ entry point down by 16 bytes, and out pops a binary that has
+ all functions profiled.
+
+ NOTE: Neither FDRs nor PDRs are strictly sorted in memory
+ order. For example, when including header-files that
+ define functions, the FDRs follow behind the including
+ file, even though their code may have been generated at
+ a lower address. File coff-alpha.c from libbfd
+ illustrates this (use "odump -PFv" to look at a file's
+ FDR/PDR). Similarly, PDRs are sometimes out of order
+ as well. An example of this is OSF/1 v3.0 libc's
+ malloc.c. I'm not sure why this happens, but it could
+ be due to optimizations that reorder a function's
+ position within an object-file.
+
+ Strategy:
+
+ On the first call to this function, we build a table of FDRs
+ that is sorted by the base-address of the object-file the FDR
+ is referring to. Notice that each object-file may contain
+ code from multiple source files (e.g., due to code defined in
+ include files). Thus, for any given base-address, there may
+ be multiple FDRs (but this case is, fortunately, uncommon).
+ lookup(addr) guarantees to return the first FDR that applies
+ to address ADDR. Thus, after invoking lookup(), we have a
+ list of FDRs that may contain the PDR for ADDR. Next, we
+ walk through the PDRs of these FDRs and locate the one that
+ is closest to ADDR (i.e., for which the difference between
+ ADDR and the PDR's entry point is positive and minimal).
+ Once, the right FDR and PDR are located, we simply walk
+ through the line-number table to lookup the line-number that
+ best matches ADDR. Obviously, things could be sped up by
+ keeping a sorted list of PDRs instead of a sorted list of
+ FDRs. However, this would increase space requirements
+ considerably, which is undesirable. */
+ external_pdr_size = debug_swap->external_pdr_size;
+
+ /* Make offset relative to object file's start-address: */
+ offset -= tab[i].base_addr;
+ /* Search FDR list starting at tab[i] for the PDR that best matches
+ OFFSET. Normally, the FDR list is only one entry long. */
+ best_fdr = NULL;
+ do
+ {
+ bfd_vma dist, min_dist = 0;
+ char *pdr_hold;
+ char *pdr_end;
+
+ fdr_ptr = tab[i].fdr;
+
+ pdr_ptr = ((char *) debug_info->external_pdr
+ + fdr_ptr->ipdFirst * external_pdr_size);
+ pdr_end = pdr_ptr + fdr_ptr->cpd * external_pdr_size;
+ (*debug_swap->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr);
+ /* Find PDR that is closest to OFFSET. If pdr.prof is set,
+ the procedure entry-point *may* be 0x10 below pdr.adr. We
+ simply pretend that pdr.prof *implies* a lower entry-point.
+ This is safe because it just means that may identify 4 NOPs
+ in front of the function as belonging to the function. */
+ for (pdr_hold = NULL;
+ pdr_ptr < pdr_end;
+ (pdr_ptr += external_pdr_size,
+ (*debug_swap->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr)))
+ {
+ if (offset >= (pdr.adr - 0x10 * pdr.prof))
+ {
+ dist = offset - (pdr.adr - 0x10 * pdr.prof);
+ if (!pdr_hold || dist < min_dist)
+ {
+ min_dist = dist;
+ pdr_hold = pdr_ptr;
+ }
+ }
+ }
+
+ if (!best_pdr || min_dist < best_dist)
+ {
+ best_dist = min_dist;
+ best_fdr = fdr_ptr;
+ best_pdr = pdr_hold;
+ }
+ /* continue looping until base_addr of next entry is different: */
+ }
+ while (++i < line_info->fdrtab_len
+ && tab[i].base_addr == tab[i - 1].base_addr);
+
+ if (!best_fdr || !best_pdr)
+ return false; /* shouldn't happen... */
+
+ /* phew, finally we got something that we can hold onto: */
+ fdr_ptr = best_fdr;
+ pdr_ptr = best_pdr;
+ (*debug_swap->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr);
+ /* Now we can look for the actual line number. The line numbers
+ are stored in a very funky format, which I won't try to
+ describe. The search is bounded by the end of the FDRs line
+ number entries. */
+ line_end = debug_info->line + fdr_ptr->cbLineOffset + fdr_ptr->cbLine;
+
+ /* Make offset relative to procedure entry: */
+ offset -= pdr.adr - 0x10 * pdr.prof;
+ lineno = pdr.lnLow;
+ line_ptr = debug_info->line + fdr_ptr->cbLineOffset + pdr.cbLineOffset;
+ while (line_ptr < line_end)
+ {
+ int delta;
+ unsigned int count;
+
+ delta = *line_ptr >> 4;
+ if (delta >= 0x8)
+ delta -= 0x10;
+ count = (*line_ptr & 0xf) + 1;
+ ++line_ptr;
+ if (delta == -8)
+ {
+ delta = (((line_ptr[0]) & 0xff) << 8) + ((line_ptr[1]) & 0xff);
+ if (delta >= 0x8000)
+ delta -= 0x10000;
+ line_ptr += 2;
+ }
+ lineno += delta;
+ if (offset < count * 4)
+ {
+ line_info->cache.stop += count * 4 - offset;
+ break;
+ }
+ offset -= count * 4;
+ }
+
+ /* If fdr_ptr->rss is -1, then this file does not have full
+ symbols, at least according to gdb/mipsread.c. */
+ if (fdr_ptr->rss == -1)
+ {
+ line_info->cache.filename = NULL;
+ if (pdr.isym == -1)
+ line_info->cache.functionname = NULL;
+ else
+ {
+ EXTR proc_ext;
+
+ (*debug_swap->swap_ext_in)
+ (abfd,
+ ((char *) debug_info->external_ext
+ + pdr.isym * debug_swap->external_ext_size),
+ &proc_ext);
+ line_info->cache.functionname = (debug_info->ssext
+ + proc_ext.asym.iss);
+ }
+ }
+ else
+ {
+ SYMR proc_sym;
+
+ line_info->cache.filename = (debug_info->ss
+ + fdr_ptr->issBase
+ + fdr_ptr->rss);
+ (*debug_swap->swap_sym_in)
+ (abfd,
+ ((char *) debug_info->external_sym
+ + ((fdr_ptr->isymBase + pdr.isym)
+ * debug_swap->external_sym_size)),
+ &proc_sym);
+ line_info->cache.functionname = (debug_info->ss
+ + fdr_ptr->issBase
+ + proc_sym.iss);
+ }
+ if (lineno == ilineNil)
+ lineno = 0;
+ line_info->cache.line_num = lineno;
+ }
+ else
+ {
+ bfd_size_type external_sym_size;
+ const char *directory_name;
+ const char *main_file_name;
+ const char *current_file_name;
+ const char *function_name;
+ const char *line_file_name;
+ bfd_vma low_func_vma;
+ bfd_vma low_line_vma;
+ boolean past_line;
+ boolean past_fn;
+ char *sym_ptr, *sym_ptr_end;
+ size_t len, funclen;
+ char *buffer = NULL;
+
+ /* This file uses stabs debugging information. When gcc is not
+ optimizing, it will put the line number information before
+ the function name stabs entry. When gcc is optimizing, it
+ will put the stabs entry for all the function first, followed
+ by the line number information. (This appears to happen
+ because of the two output files used by the -mgpopt switch,
+ which is implied by -O). This means that we must keep
+ looking through the symbols until we find both a line number
+ and a function name which are beyond the address we want. */
+
+ line_info->cache.filename = NULL;
+ line_info->cache.functionname = NULL;
+ line_info->cache.line_num = 0;
+
+ directory_name = NULL;
+ main_file_name = NULL;
+ current_file_name = NULL;
+ function_name = NULL;
+ line_file_name = NULL;
+ low_func_vma = 0;
+ low_line_vma = 0;
+ past_line = false;
+ past_fn = false;
+
+ external_sym_size = debug_swap->external_sym_size;
+
+ sym_ptr = ((char *) debug_info->external_sym
+ + (fdr_ptr->isymBase + 2) * external_sym_size);
+ sym_ptr_end = sym_ptr + (fdr_ptr->csym - 2) * external_sym_size;
+ for (;
+ sym_ptr < sym_ptr_end && (! past_line || ! past_fn);
+ sym_ptr += external_sym_size)
+ {
+ SYMR sym;
+
+ (*debug_swap->swap_sym_in) (abfd, sym_ptr, &sym);
+
+ if (ECOFF_IS_STAB (&sym))
+ {
+ switch (ECOFF_UNMARK_STAB (sym.index))
+ {
+ case N_SO:
+ main_file_name = current_file_name =
+ debug_info->ss + fdr_ptr->issBase + sym.iss;
+
+ /* Check the next symbol to see if it is also an
+ N_SO symbol. */
+ if (sym_ptr + external_sym_size < sym_ptr_end)
+ {
+ SYMR nextsym;
+
+ (*debug_swap->swap_sym_in) (abfd,
+ sym_ptr + external_sym_size,
+ &nextsym);
+ if (ECOFF_IS_STAB (&nextsym)
+ && ECOFF_UNMARK_STAB (nextsym.index) == N_SO)
+ {
+ directory_name = current_file_name;
+ main_file_name = current_file_name =
+ debug_info->ss + fdr_ptr->issBase + nextsym.iss;
+ sym_ptr += external_sym_size;
+ }
+ }
+ break;
+
+ case N_SOL:
+ current_file_name =
+ debug_info->ss + fdr_ptr->issBase + sym.iss;
+ break;
+
+ case N_FUN:
+ if (sym.value > offset)
+ past_fn = true;
+ else if (sym.value >= low_func_vma)
+ {
+ low_func_vma = sym.value;
+ function_name =
+ debug_info->ss + fdr_ptr->issBase + sym.iss;
+ }
+ break;
+ }
+ }
+ else if (sym.st == stLabel && sym.index != indexNil)
+ {
+ if (sym.value > offset)
+ past_line = true;
+ else if (sym.value >= low_line_vma)
+ {
+ low_line_vma = sym.value;
+ line_file_name = current_file_name;
+ line_info->cache.line_num = sym.index;
+ }
+ }
+ }
+
+ if (line_info->cache.line_num != 0)
+ main_file_name = line_file_name;
+
+ /* We need to remove the stuff after the colon in the function
+ name. We also need to put the directory name and the file
+ name together. */
+ if (function_name == NULL)
+ len = funclen = 0;
+ else
+ len = funclen = strlen (function_name) + 1;
+
+ if (main_file_name != NULL
+ && directory_name != NULL
+ && main_file_name[0] != '/')
+ len += strlen (directory_name) + strlen (main_file_name) + 1;
+
+ if (len != 0)
+ {
+ if (line_info->find_buffer != NULL)
+ free (line_info->find_buffer);
+ buffer = (char *) bfd_malloc (len);
+ if (buffer == NULL)
+ return false;
+ line_info->find_buffer = buffer;
+ }
+
+ if (function_name != NULL)
+ {
+ char *colon;
+
+ strcpy (buffer, function_name);
+ colon = strchr (buffer, ':');
+ if (colon != NULL)
+ *colon = '\0';
+ line_info->cache.functionname = buffer;
+ }
+
+ if (main_file_name != NULL)
+ {
+ if (directory_name == NULL || main_file_name[0] == '/')
+ line_info->cache.filename = main_file_name;
+ else
+ {
+ sprintf (buffer + funclen, "%s%s", directory_name,
+ main_file_name);
+ line_info->cache.filename = buffer + funclen;
+ }
+ }
+ }
+
+ return true;
+}
+
+/* Do the work of find_nearest_line. */
+
+boolean
+_bfd_ecoff_locate_line (abfd, section, offset, debug_info, debug_swap,
+ line_info, filename_ptr, functionname_ptr, retline_ptr)
+ bfd *abfd;
+ asection *section;
+ bfd_vma offset;
+ struct ecoff_debug_info * const debug_info;
+ const struct ecoff_debug_swap * const debug_swap;
+ struct ecoff_find_line *line_info;
+ const char **filename_ptr;
+ const char **functionname_ptr;
+ unsigned int *retline_ptr;
+{
+ offset += section->vma;
+
+ if (line_info->cache.sect == NULL
+ || line_info->cache.sect != section
+ || offset < line_info->cache.start
+ || offset >= line_info->cache.stop)
+ {
+ line_info->cache.sect = section;
+ line_info->cache.start = offset;
+ line_info->cache.stop = offset;
+ if (! lookup_line (abfd, debug_info, debug_swap, line_info))
+ {
+ line_info->cache.sect = NULL;
+ return false;
+ }
+ }
+
+ *filename_ptr = line_info->cache.filename;
+ *functionname_ptr = line_info->cache.functionname;
+ *retline_ptr = line_info->cache.line_num;
+
+ return true;
+}
+\f
+/* These routines copy symbolic information into a memory buffer.
+
+ FIXME: The whole point of the shuffle code is to avoid storing
+ everything in memory, since the linker is such a memory hog. This
+ code makes that effort useless. It is only called by the MIPS ELF
+ code when generating a shared library, so it is not that big a
+ deal, but it should be fixed eventually. */
+
+/* Collect a shuffle into a memory buffer. */
+
+static boolean ecoff_collect_shuffle PARAMS ((struct shuffle *, bfd_byte *));
+
+static boolean
+ecoff_collect_shuffle (l, buff)
+ struct shuffle *l;
+ bfd_byte *buff;
+{
+ unsigned long total;
+
+ total = 0;
+ for (; l != (struct shuffle *) NULL; l = l->next)
+ {
+ if (! l->filep)
+ memcpy (buff, l->u.memory, l->size);
+ else
+ {
+ if (bfd_seek (l->u.file.input_bfd, l->u.file.offset, SEEK_SET) != 0
+ || bfd_read (buff, 1, l->size, l->u.file.input_bfd) != l->size)
+ return false;
+ }
+ total += l->size;
+ buff += l->size;
+ }
+
+ return true;
+}
+
+/* Copy PDR information into a memory buffer. */
+
+boolean
+_bfd_ecoff_get_accumulated_pdr (handle, buff)
+ PTR handle;
+ bfd_byte *buff;
+{
+ struct accumulate *ainfo = (struct accumulate *) handle;
+
+ return ecoff_collect_shuffle (ainfo->pdr, buff);
+}
+
+/* Copy symbol information into a memory buffer. */
+
+boolean
+_bfd_ecoff_get_accumulated_sym (handle, buff)
+ PTR handle;
+ bfd_byte *buff;
+{
+ struct accumulate *ainfo = (struct accumulate *) handle;
+
+ return ecoff_collect_shuffle (ainfo->sym, buff);
+}
+
+/* Copy the string table into a memory buffer. */
+
+boolean
+_bfd_ecoff_get_accumulated_ss (handle, buff)
+ PTR handle;
+ bfd_byte *buff;
+{
+ struct accumulate *ainfo = (struct accumulate *) handle;
+ struct string_hash_entry *sh;
+ unsigned long total;
+
+ /* The string table is written out from the hash table if this is a
+ final link. */
+ BFD_ASSERT (ainfo->ss == (struct shuffle *) NULL);
+ *buff++ = '\0';
+ total = 1;
+ BFD_ASSERT (ainfo->ss_hash == NULL || ainfo->ss_hash->val == 1);
+ for (sh = ainfo->ss_hash;
+ sh != (struct string_hash_entry *) NULL;
+ sh = sh->next)
+ {
+ size_t len;
+
+ len = strlen (sh->root.string);
+ memcpy (buff, (PTR) sh->root.string, len + 1);
+ total += len + 1;
+ buff += len + 1;
+ }
return true;
}