Fix the reading of the debugging information of Tru64/Alpha binaries that

are produced by recent Compaq compilers.
This commit is contained in:
Nick Clifton 2002-12-16 16:25:06 +00:00
parent 30cdfbed02
commit af738ea75a
2 changed files with 169 additions and 42 deletions

View File

@ -1,3 +1,19 @@
2002-12-16 Nathan Tallent <eraxxon@alumni.rice.edu>
* ecofflink.c: Fix the reading of the debugging information
of Tru64/Alpha binaries that are produced by recent Compaq
compilers.
(mk_fdrtab): Fix error in creating the FDR (file descriptor)
table.
(lookup_line): Because of the strange information sometimes
generated by Compaq's recent compilers, change how the FDR
table is searched so that PDRs (procedure descriptors) are
correctly found. Note that this change is really more of a hack;
however, I have included extensive documentation as to why
this is the best solution short of an extensive rewrite or
another hack.
(fdrtab_lookup): Add comments to explain the algorithm.
2002-12-12 Alexandre Oliva <aoliva@redhat.com>
* elf-m10300.c (elf32_mn10300_link_hash_newfunc): Reorder

View File

@ -3,21 +3,21 @@
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.
This file is part of BFD, the Binary File Descriptor library.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "bfd.h"
#include "sysdep.h"
@ -1836,16 +1836,15 @@ mk_fdrtab (abfd, debug_info, debug_swap, line_info)
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: */
/* 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 */
if (fdr_ptr->cpd == 0) /* Skip FDRs that have no PDRs. */
continue;
++len;
}
/* Now, create and fill in the table: */
/* Now, create and fill in the table. */
amt = (bfd_size_type) len * sizeof (struct ecoff_fdrtab_entry);
line_info->fdrtab = (struct ecoff_fdrtab_entry*) bfd_zalloc (abfd, amt);
if (line_info->fdrtab == NULL)
@ -1868,7 +1867,7 @@ mk_fdrtab (abfd, debug_info, debug_swap, line_info)
SYMR sym;
sym_ptr = ((char *) debug_info->external_sym
+ (fdr_ptr->isymBase + 1)*debug_swap->external_sym_size);
+ (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)
@ -1877,23 +1876,37 @@ mk_fdrtab (abfd, debug_info, debug_swap, line_info)
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);
/* eraxxon: There are at least two problems with this computation:
1) PDRs do *not* contain offsets but full vma's; and typically the
address of the first PDR is the address of the FDR, which will
make (most) of the results of the original computation 0!
2) Once in a wacky while, the Compaq compiler generated PDR
addresses do not equal the FDR vma, but they (the PDR address)
are still vma's and not offsets. Cf. comments in
'lookup_line'. */
#if 0
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;
tab->base_addr = fdr_ptr->adr - pdr.adr;
#else
/* 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;
#endif
}
else
{
/* XXX I don't know about stabs, so this is a guess
(davidm@cs.arizona.edu): */
(davidm@cs.arizona.edu). */
tab->base_addr = fdr_ptr->adr;
}
tab->fdr = fdr_ptr;
@ -1937,14 +1950,22 @@ fdrtab_lookup (line_info, offset)
else
low = mid + 1;
}
/* eraxxon: at this point 'offset' is either lower than the lowest entry or
higher than the highest entry. In the former case high = low = mid = 0;
we want to return -1. In the latter case, low = high and mid = low - 1;
we want to return the index of the highest entry. Only in former case
will the following 'catch-all' test be true. */
++mid;
/* last entry is catch-all for all higher addresses: */
/* Last entry is catch-all for all higher addresses. */
if (offset < tab[mid].base_addr)
return -1;
find_min:
/* eraxxon: There may be multiple FDRs in the table with the
same base_addr; make sure that we are at the first one. */
while (mid > 0 && tab[mid - 1].base_addr == tab[mid].base_addr)
--mid;
@ -1967,6 +1988,7 @@ lookup_line (abfd, debug_info, debug_swap, line_info)
FDR *fdr_ptr;
int i;
/* eraxxon: note that 'offset' is the full vma, not a section offset. */
offset = line_info->cache.start;
/* Build FDR table (sorted by object file's base-address) if we
@ -1977,10 +1999,80 @@ lookup_line (abfd, debug_info, debug_swap, line_info)
tab = line_info->fdrtab;
/* find first FDR for address OFFSET */
/* Find first FDR for address OFFSET. */
i = fdrtab_lookup (line_info, offset);
if (i < 0)
return FALSE; /* no FDR, no fun... */
/* eraxxon: 'fdrtab_lookup' doesn't give what we want, at least for Compaq's
C++ compiler 6.2. Consider three FDRs with starting addresses of x, y,
and z, respectively, such that x < y < z. Assume further that
y < 'offset' < z. It is possble at times that the PDR for 'offset' is
associated with FDR x and *not* with FDR y. Erg!!
From a binary dump of my C++ test case 'moo' using Compaq's coffobjanl
(output format has been edited for our purposes):
FDR [2]: (main.C): First instruction: 0x12000207c <x>
PDR [5] for File [2]: LoopTest__Xv <0x1200020a0> (a)
PDR [7] for File [2]: foo__Xv <0x120002168>
FDR [1]: (-1): First instruction: 0x1200020e8 <y>
PDR [3] for File [1]: <0x120001ad0> (b)
FDR [6]: (-1): First instruction: 0x1200026f0 <z>
(a) In the case of PDR5, the vma is such that the first few instructions
of the procedure can be found. But since the size of this procedure is
160b, the vma will soon cross into the 'address space' of FDR1 and no
debugging info will be found. How repugnant!
(b) It is also possible for a PDR to have a *lower* vma than its associated
FDR; see FDR1 and PDR3. Gross!
Since the FDRs that are causing so much havok (in this case) 1) do not
describe actual files (fdr.rss == -1), and 2) contain only compiler
genarated routines, I thought a simple fix would be to exclude them from
the FDR table in 'mk_fdrtab'. But, besides not knowing for certain
whether this would be correct, it creates an additional problem. If we
happen to ask for source file info on a compiler generated (procedure)
symbol -- which is still in the symbol table -- the result can be
information from a real procedure! This is because compiler generated
procedures with vma's higher than the last FDR in the fdr table will be
associated with a PDR from this FDR, specifically the PDR with the
highest vma. This wasn't a problem before, because each procedure had a
PDR. (Yes, this problem could be eliminated if we kept the size of the
last PDR around, but things are already getting ugly).
Probably, a better solution would be to have a sorted PDR table. Each
PDR would have a pointer to its FDR so file information could still be
obtained. A FDR table could still be constructed if necessary -- since
it only contains pointers, not much extra memory would be used -- but
the PDR table would be searched to locate debugging info.
There is still at least one remaining issue. Sometimes a FDR can have a
bogus name, but contain PDRs that should belong to another FDR with a
real name. E.g:
FDR [3]: 0000000120001b50 (/home/.../Array.H~alt~deccxx_5E5A62AD)
PDR [a] for File [3]: 0000000120001b50
PDR [b] for File [3]: 0000000120001cf0
PDR [c] for File [3]: 0000000120001dc8
PDR [d] for File [3]: 0000000120001e40
PDR [e] for File [3]: 0000000120001eb8
PDR [f] for File [3]: 0000000120001f4c
FDR [4]: 0000000120001b50 (/home/.../Array.H)
Here, FDR4 has the correct name, but should (seemingly) contain PDRa-f.
The symbol table for PDR4 does contain symbols for PDRa-f, but so does
the symbol table for FDR3. However the former is different; perhaps this
can be detected easily. (I'm not sure at this point.) This problem only
seems to be associated with files with templates. I am assuming the idea
is that there is a 'fake' FDR (with PDRs) for each differently typed set
of templates that must be generated. Currently, FDR4 is completely
excluded from the FDR table in 'mk_fdrtab' because it contains no PDRs.
Since I don't have time to prepare a real fix for this right now, be
prepared for 'A Horrible Hack' to force the inspection of all non-stabs
FDRs. It's coming... */
fdr_ptr = tab[i].fdr;
/* Check whether this file has stabs debugging information. In a
@ -2006,7 +2098,7 @@ lookup_line (abfd, debug_info, debug_swap, line_info)
char *pdr_ptr;
char *best_pdr = NULL;
FDR *best_fdr;
bfd_vma best_dist = ~(bfd_vma) 0;
bfd_signed_vma best_dist = -1;
PDR pdr;
unsigned char *line_ptr;
unsigned char *line_end;
@ -2068,14 +2160,29 @@ lookup_line (abfd, debug_info, debug_swap, line_info)
considerably, which is undesirable. */
external_pdr_size = debug_swap->external_pdr_size;
/* Make offset relative to object file's start-address: */
#if 0 /* eraxxon: PDR addresses (pdr.adr) are not relative to FDRs!
Leave 'offset' alone. */
/* Make offset relative to object file's start-address. */
offset -= tab[i].base_addr;
#endif
/* eraxxon: The Horrible Hack: Because of the problems above, set 'i'
to 0 so we look through all FDRs.
Because FDR's without any symbols are assumed to be non-stabs,
searching through all FDRs may cause the following code to try to
read stabs FDRs as ECOFF ones. However, I don't think this will
harm anything. */
i = 0;
/* 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;
/* eraxxon: 'dist' and 'min_dist' can be negative now
because we iterate over every FDR rather than just ones
with a base address less than or equal to 'offset'. */
bfd_signed_vma dist = -1, min_dist = -1;
char *pdr_hold;
char *pdr_end;
@ -2098,7 +2205,10 @@ lookup_line (abfd, debug_info, debug_swap, line_info)
if (offset >= (pdr.adr - 0x10 * pdr.prof))
{
dist = offset - (pdr.adr - 0x10 * pdr.prof);
if (!pdr_hold || dist < min_dist)
/* eraxxon: 'dist' can be negative now. Note that
'min_dist' can be negative if 'pdr_hold' below is NULL. */
if (!pdr_hold || (dist >= 0 && dist < min_dist))
{
min_dist = dist;
pdr_hold = pdr_ptr;
@ -2106,21 +2216,22 @@ lookup_line (abfd, debug_info, debug_swap, line_info)
}
}
if (!best_pdr || min_dist < best_dist)
if (!best_pdr || (min_dist >= 0 && min_dist < best_dist))
{
best_dist = min_dist;
best_dist = (bfd_vma) min_dist;
best_fdr = fdr_ptr;
best_pdr = pdr_hold;
}
/* continue looping until base_addr of next entry is different: */
/* 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);
/* eraxxon: We want to iterate over all FDRs.
See previous comment about 'fdrtab_lookup'. */
while (++i < line_info->fdrtab_len);
if (!best_fdr || !best_pdr)
return FALSE; /* shouldn't happen... */
return FALSE; /* Shouldn't happen... */
/* phew, finally we got something that we can hold onto: */
/* 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);
@ -2130,7 +2241,7 @@ lookup_line (abfd, debug_info, debug_swap, line_info)
number entries. */
line_end = debug_info->line + fdr_ptr->cbLineOffset + fdr_ptr->cbLine;
/* Make offset relative to procedure entry: */
/* 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;