Bug 11610 - _init/_fini do not have proper unwinding information
Summary: _init/_fini do not have proper unwinding information
Status: ASSIGNED
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: 2.12
: P2 normal
Target Milestone: ---
Assignee: Paul Pluzhnikov
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-05-19 14:44 UTC by Petr Baudis
Modified: 2014-06-30 18:04 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments
Proposed patch by Michael Matz (1.06 KB, patch)
2010-05-19 14:45 UTC, Petr Baudis
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Petr Baudis 2010-05-19 14:44:47 UTC
It is impossible to reliably unwind the stack above _fini() on x86_64 since no
unwind information is provided for it and it modifies a stack register. This
matters for gdb backtracing - if a process crashes within a destructor, it can
frequently be essential to look at why the program began terminating in the
first place.
Comment 1 Petr Baudis 2010-05-19 14:45:37 UTC
Created attachment 4800 [details]
Proposed patch by Michael Matz
Comment 2 Paul Pluzhnikov 2012-10-26 21:30:20 UTC
This has just bit me as well.

The proposed patch no longer works, as initfini.c has been replaced with sysdeps/x86_64/crti.S and sysdeps/i386/crti.S

The same problem now exists on i686 as well.

This is not just a GDB problem either: if I dlopen a library in a try/catch,
and the library ctor throws, that throw is not caught:

// g++ -g main.cc -ldl
#include <dlfcn.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
  try {
    void *h = dlopen("./foo.so", RTLD_LAZY);
  } catch (...) {
    fprintf(stderr, "got exception\n");
    return 1;
  }
  return 0;
}

// g++ -g -fPIC -shared -o foo.so foo.cc
#include <stdio.h>

static void __attribute__((constructor))
foo(void)
{
  fprintf(stderr, "in foo\n");
  throw 1;
}


./a.out
in foo
terminate called after throwing an instance of 'int'
Aborted


I'll send a new patch to libc-alpha.
Comment 3 Paul Pluzhnikov 2012-10-27 02:40:52 UTC
(In reply to comment #2)

> This is not just a GDB problem either: if I dlopen a library in a try/catch,
> and the library ctor throws, that throw is not caught:

This turns out to be because GLIBC dlopen has empty throw clause,
and the test failed even with fixed crti.S.
Comment 4 Rich Felker 2012-10-28 00:01:20 UTC
I'm curious what the "right" behavior in this circumstance would be. Are you suggesting dlopen should catch the exception and back out the whole operation? I question whether that's possible/safe at the point where ctors are already being called. If dlopen did not catch it but just let the caller catch it, what would the caller be expected to do? Throwing exceptions from global ctors (well, even having global ctors to begin with, but that's another topic entirely) just strikes me as a flawed library design.
Comment 5 Paul Pluzhnikov 2012-10-28 00:16:36 UTC
(In reply to comment #4)
> I'm curious what the "right" behavior in this circumstance would be. Are you
> suggesting dlopen should catch the exception and back out the whole operation?

No, I am not suggesting that.

> I question whether that's possible/safe at the point where ctors are already
> being called. If dlopen did not catch it but just let the caller catch it, what
> would the caller be expected to do?

Whatever it would have done if dlopen() was a direct call to foo() instead.

The bottom line is that if you are in a try/catch, and an exception is thrown, and that exception doesn't make it to your catch, then your language support is arguably busted/incomplete.

> Throwing exceptions from global ctors
> (well, even having global ctors to begin with, but that's another topic
> entirely) just strikes me as a flawed library design.

No argument there. Please note that the entire dlopen/throw example was constructed to demonstrate that the lack of unwind info in _init is not just a GDB problem, but a potential problem in other contexts as well.
Comment 6 Rich Felker 2012-10-28 00:47:27 UTC
> > I question whether that's possible/safe at the point where ctors are already
> > being called. If dlopen did not catch it but just let the caller catch it, what
> > would the caller be expected to do?
> 
> Whatever it would have done if dlopen() was a direct call to foo() instead.

This makes no sense. Unless dlopen is handling the exception, the internal state of the dynamic linker is in an inconsistent state, possibly with locks still held and some libraries loaded but without their constructors having been called.

Note that the caller has no way to even get the library handle and pass it to dlclose, because dlopen did not return.

> The bottom line is that if you are in a try/catch, and an exception is thrown,
> and that exception doesn't make it to your catch, then your language support is
> arguably busted/incomplete.

I disagree. Your catch is separated from the throw by multiple foreign function calls. dlopen is not part of the C++ language; it's part of POSIX, which has no relationship with C++, and is under no obligation to allow exceptions to propagate across it. If I'm not mistaken, the C++ standard makes it explicit that throwing an exception in C++ code that was reached as a callback from a C function results in undefined behavior if the exception propagates out across the C/C++ boundary.
Comment 7 Andreas Schwab 2013-02-04 13:32:10 UTC
This should no longer be an issue with the use of .init/fini_array.