Fix the reading of the debugging information of Tru64/Alpha binaries that
are produced by recent Compaq compilers.
This commit is contained in:
parent
30cdfbed02
commit
af738ea75a
2 changed files with 169 additions and 42 deletions
|
@ -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
|
||||
|
|
149
bfd/ecofflink.c
149
bfd/ecofflink.c
|
@ -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)
|
||||
|
@ -1877,6 +1876,15 @@ mk_fdrtab (abfd, debug_info, debug_swap, line_info)
|
|||
|
||||
if (!stabs)
|
||||
{
|
||||
/* 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;
|
||||
|
@ -1889,11 +1897,16 @@ mk_fdrtab (abfd, debug_info, debug_swap, line_info)
|
|||
/* 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
|
||||
/* 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;
|
||||
|
|
Loading…
Reference in a new issue