Re: [rfa/mips] Stop backtraces when we've lost the PC

Daniel Jacobowitz
2004-03-08T20:26:00Z

On Mon, Mar 08, 2004 at 10:48:14AM -0500, Daniel Jacobowitz wrote:
> OK, two things.
>
>
> The first problem is that __thread_start is a local symbol, and the
> copy of the library I'm using on my target is stripped of local symbols
> but has a .pdr section. We pass the PC to find_proc_desc, which is
> fine, but non_heuristic_proc_desc does:
> find_pc_partial_function (pc, NULL, &startaddr, NULL);
> and looks for a PDR associated with startaddr. Oopsie.
>
> The problem is, GAS's .pdr information does not include a size or an
> end address. It's slightly risky to search for the latest .pdr entry
> preceding PC, because that might be for a completely different
> function, but not very risky - .frame will generally be used
> consistently if it was used at all.
>
> I believe the attached patch is safe in a superset of circumstances to
> where the previous code was safe. It causes us to select the correct
> .pdr entry in this circumstance. What do you think of it?
>
> [BTW: I verified that without a .pdr section we notice that we can't
> find a prologue for clone, and give up unwinding semi-gracefully. I
> also verified that with local symbols, we find the correct .pdr.]
>
>
> The other thing:
>
> I hypothesize that if two consecutive frames, regardless of their type,
> claim to save the PC register at the same location, then unwinding is
> hosed. If we changed the interface for gdbarch_unwind_pc to return
> information on where, if anywhere, the PC was unwound from, we could
> check this. This handles sigtramp frames (which save the PC at a
> particular memory buffer), dummy frames (which would say not_lval, i.e.
> the PC is not saved in a simple fashion, i.e. skip this test), typical
> normal frames (the sentinel frame says it's saved in a register, the
> outermost frame says it's saved in the return address register or in a
> local stack slot, normal frames which called other normal frames will
> have it in a local stack slot.
>
> Obviously for some architecture with a reconstructed not_lval PC this
> wouldn't work, but I think it could still be useful as a safety check
> on the unwinder. Not a safe termination - this sort of hack doesn't
> have any business being relied on - but at least a call to error() to
> prevent spiralling off into infinity.
>
> Does my hypothesis make sense? Shall I try to implement it?

I stand by the theory but there was a problem in the implementation;
several, actually, related to dynamic resolution stubs. Which are next
on my list of things that need to get taken care of. Right now, with
or without my patch, they pick up the .pdr entry describing the last
full section before the PLT. An unfortunate consequence of their being
in .text on MIPS. So a little care is needed to mitigate the
consequences.

Corrected patch attached. Fully tested on mips-linux this time.

--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer

2004-03-08 Daniel Jacobowitz <drow@mvista.com>

* mips-tdep.c (non_heuristic_proc_desc): Search using the specified
PC rather than the partial function start address. Use the start
address to sanity check the found PDR.

Index: mips-tdep.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/mips-tdep.c,v
retrieving revision 1.283
diff -u -p -r1.283 mips-tdep.c
--- mips-tdep.c 17 Feb 2004 15:21:21 -0000 1.283
+++ mips-tdep.c 8 Mar 2004 18:50:58 -0000
@@ -2352,38 +2352,63 @@ non_heuristic_proc_desc (CORE_ADDR pc, C
{
int low, mid, high;
char *ptr;
+ CORE_ADDR pdr_pc;

low = 0;
high = priv->size / 32;

+ /* First, find the last .pdr entry starting at or before PC. */
do
{
- CORE_ADDR pdr_pc;
-
mid = (low + high) / 2;

ptr = priv->contents + mid * 32;
pdr_pc = bfd_get_signed_32 (sec->objfile->obfd, ptr);
pdr_pc += ANOFFSET (sec->objfile->section_offsets,
SECT_OFF_TEXT (sec->objfile));
- if (pdr_pc == startaddr)
+ if (pdr_pc == pc)
break;
- if (pdr_pc > startaddr)
+ if (pdr_pc > pc)
high = mid;
else
low = mid + 1;
}
while (low != high);

- if (low != high)
+ /* If we got an exact match on PC, low != high. Otherwise,
+ both low and high point one past the PDR of interest.
+ If both are zero, that means this PC is before any
+ region covered by a PDR. */
+ if (low == high && low > 0)
+ {
+ ptr = priv->contents + (low - 1) * 32;
+ pdr_pc = bfd_get_signed_32 (sec->objfile->obfd, ptr);
+ pdr_pc += ANOFFSET (sec->objfile->section_offsets,
+ SECT_OFF_TEXT (sec->objfile));
+ }
+
+ /* We don't have a range, so we have no way to know for sure
+ whether we're in the correct PDR or a PDR for a preceding
+ function and the current function was a stripped local
+ symbol. But if the PDR's PC is at least as great as the
+ best guess from the symbol table, assume that it does cover
+ the right area; if a .pdr section is present at all then
+ nearly every function will have an entry. The biggest exception
+ will be the dynamic linker stubs; conveniently these are
+ placed before .text instead of after. */
+
+ if (pc >= pdr_pc && pdr_pc >= startaddr)
{
struct symbol *sym = find_pc_function (pc);

+ if (addrptr)
+ *addrptr = pdr_pc;
+
/* Fill in what we need of the proc_desc. */
proc_desc = (mips_extra_func_info_t)
obstack_alloc (&sec->objfile->objfile_obstack,
sizeof (struct mips_extra_func_info));
- PROC_LOW_ADDR (proc_desc) = startaddr;
+ PROC_LOW_ADDR (proc_desc) = pdr_pc;

/* Only used for dummy frames. */
PROC_HIGH_ADDR (proc_desc) = 0;