Re: [rfc/rfa] Use ARM exception tables as GDB unwinder

Ulrich Weigand
2010-10-21T15:51:05Z

Dan Jacobowitz wrote:

> On Wed, Oct 20, 2010 at 02:00:56AM +0200, Ulrich Weigand wrote:
> > In addition, one minor change was necessary to the prologue unwinder:
> > the this_id routine no longer uses get_frame_func to retrieve the
> > function start address; instead, this value is stored in the cache.
> > This allows the exception unwinder to provide this information from
> > the exception table, instead of having to rely on symbol data.
>
> Like Matthew, I have some concern about this; unwind regions are not
> 1:1 with functions. Not only will the linker combine identical
> regions, but hand-written code (or, hypothetically,
> -fnon-call-exceptions) can have multiple regions in a function.
> But we can probably ignore the latter case; the former can be handled
> by using whichever is larger, the symbol or exception region address.

This is in effect what happens, because if the symbol is larger than
the exception region address, I'm refusing to use the unwind record
and fall back to prologue parsing (which will use the symbol).

> > + /* See if we have an ARM exception table entry covering this address. */
> > + addr_in_block = get_frame_address_in_block (this_frame);
> > + entry = arm_find_exidx_entry (addr_in_block, &func_exidx);
> > + if (!entry)
> > + return 0;
>
> I assume you're deliberately treating can't-unwind and no-information
> the same. We use can't-unwind to mark _start, but it also shows up in
> exception handling in some cases.

Hmm, I'm seeing many functions marked as can't-unwind in the Ubuntu
copy of glibc, so I was assuming this just means that for some reason
the unwind data couldn't be generated reliably enough to allow run-time
unwinding. However, for debugging purposes we still want to attempt to
show a backtrace, so I'm falling back to prologue parsing ...

> > + /* The ARM exception index does not mark the *end* of the function
> > + covered by the entry, and some functions will not have any entry.
> > + Therefore, the entry we found above may not actually be correct
> > + for this PC. As a sanity check, also try to determine the function
> > + covering PC via the symbol table. If this finds a function start
> > + address *closer* to PC than the one found via the exception table,
> > + ignore the exception record, and fall back to prologue parsing.
> > +
> > + (Note that if we don't find a function via the symbol table, the
> > + exception table may still be wrong. But in this case, prologue
> > + parsing wouldn't work anyway, so we use the exception table and
> > + hope for the best.) */
> > + if (find_pc_partial_function (addr_in_block, NULL, &func_symtab, NULL)
> > + && func_symtab > func_exidx)
> > + return 0;
>
> This doesn't go well with the discussion above about combined
> exception regions... did you encounter this problem in practice? The
> linker used to have this problem, but hasn't for a while (circa
> 2009-05-05).

Yes, I've definitely seen this in practice. In the Ubuntu glibc I have:

Unwind table index '.ARM.exidx' at offset 0xe8448 contains 596 entries:

0x15470: 0x800b3fac
Compact model 0
0x0b vsp = vsp + 48
0x3f vsp = vsp + 256
0xac pop {r4, r5, r6, r7, r8r14}

0x157ec: 0x80aab0b0
Compact model 0
0xaa pop {r4, r5, r6r14}
0xb0 finish
0xb0 finish

0x1584c: 0x8003a8b0
Compact model 0
0x03 vsp = vsp + 16
0xa8 pop {r4r14}
0xb0 finish

0x15890: 0x808408b0
Compact model 0
0x84 0x08 pop {r7, r14}
0xb0 finish

0x25a50: 0x808400b0
Compact model 0
0x84 0x00 pop {r14}
0xb0 finish

0x25a90: 0x80a8b0b0
Compact model 0
0xa8 pop {r4r14}
0xb0 finish
0xb0 finish

Note the large gap between 0x15890 and 0x25a50, which contains many
functions, including "raise", for which the "pop {r7, r14}" instruction
certainly isn't correct. This caused test suite failures before I
added the check above.

I understood the ARM standard to say that this was expected, because
for C or assembly routines, presence of an unwind entry is optional.
Is this not the case?

> The actual unwinder looks OK to me. If you want to handle C++ code
> with exceptions (or C with cleanups), you'll need to check the name of
> the referenced personality routine; it is __gxx_personality_v0 (et
> cetera). Readelf has a sample of that. Code which doesn't throw or
> catch uses the standard personality routines, as you've implemented.

Ah, I didn't see the readelf code (because I was looking at an old
checkout, I would appear). I can add those personality routines as well
(however, these are nowhere used in the glibc I'm looking at).

Thanks for the review!