Excessive calls to iterate_phdr during exception handling
Ryan Johnson
ryan.johnson@cs.utoronto.ca
Wed May 29 03:50:00 GMT 2013
On 28/05/2013 11:05 PM, Alan Modra wrote:
> On Tue, May 28, 2013 at 09:19:48PM -0400, Ryan Johnson wrote:
>> On 28/05/2013 8:47 PM, Ian Lance Taylor wrote:
>>> On Mon, May 27, 2013 at 3:20 PM, Ryan Johnson
>>> <ryan.johnson@cs.utoronto.ca> wrote:
>>>> I'm bringing the issue up here, rather than filing a bug, because I'm not
>>>> sure whether this is an oversight, a known problem that's hard to fix, or a
>>>> feature (e.g. somehow required for reliable unwinding). I suspect the
>>>> former, because _Unwind_Find_FDE tries a call to _Unwind_Find_registered_FDE
>>>> before falling back to dl_iterate_phdr, but the former never succeeds in my
>>>> trace (iterate_phdr is always called).
>>> The issue is dlclose followed by dlopen. If we had a cache ahead of
>>> dl_iterate_phdr, we would need some way to clear out any information
>>> cached from a dlclose'd library. Otherwise we might pick up the old
>>> information when looking up an address from a new dlopen. So 1)
>>> locking will always be required; 2) any caching system to reduce the
>>> number of locks will require support for dlclose, somehow. It's worth
>>> working on but there isn't going to be a simple solution.
>> I have mixed feelings on this... on the one had it would be bad to
>> risk sending unwind off to la-la land because somebody did a quick
>> dlclose/dlopen pair on code we're about to unwind through... but on
>> the other hand anybody who does a dlclose/dlopen pair on code we're
>> about to unwind through (a) is asking for trouble and (b) is
>> perfectly free to do so in spite of the mutex [1].
> Yes of course you can shoot yourself in the foot. The mutex is there
> to stop the glibc dl_iterate_phdr list traversal running awry when
> dlopen/dlclose happens in another thread. To be clear, I'm talking
> about a dlclose on an object that your thread doesn't access. Such a
> dlclose shouldn't affect your thread in any way. But if glibc's
> list of loaded objects was allowed to change while your thread was
> running dl_iterate_phdr, then dl_iterate_phdr could potentially read
> freed list entries.
Understood. It's only safe to populate a cache with header info if you
hold the loader mutex.
... but once you have that cache, the rest of unwind is up for grabs...
For example, it should be reasonably safe to let
__cxa_allocate_exception call dl_iterate_phdr in order to build a list
of object headers valid at the time unwind begins. It already calls
malloc, so allocating space for a cache (holding pointers to at most a
few dozen object headers) wouldn't be so terrible; __cxa_free_exception
could release the space after the dust settles. In order to optimize
non-throw uses of unwinding, it might make sense to build the cache
somewhere besides __cxa_allocate_exception, but the basic concept
doesn't change. During unwind, _Unwind_find_FDE could work exclusively
from the cache instead of calling dl_iterate_phdr, reducing call count
for the latter from 4-6 per stack frame unwound to one per throw, which
seems like a pretty big win [1].
A more aggressive optimization would stash the the header cache in TLS
between uses, rebuilding only if dladd/dlsub count changes. That would
require a lock-free method to access the two counts, however, which I
don't think currently exists. There would be a race between reading the
two variables, and another between read and use of either variable, but
the whole race window arises during a time when any change that mattered
would have dire consequences whether the cache is stale or not.
[1] I'd also move the call to _Unwind_Find_registered_FDE after the
cache check, so it becomes a fallback (thus avoiding the libgcc mutex
lock in the common case of "newer" executables and shared libraries).
Unless by some cruel twist of history, there exist out there objects
that register their FDEs manually *and* provide .eh_frame whose invalid
entries must be ignored?
Ryan
More information about the Gcc
mailing list