Hi Dale,
this is the bit I don't understand. Why does it go
into a loop? How can the unwinder possibly know that
the original code did not have a catch-all, since we
tell it which catches there are and we say: there is
a catch-all!
The unwinder works by doing a stack crawl to find a handler. Since
we're telling it every eh-selector is a catch-all handler, it finds
one immediately (that is not there)
I'm not sure what you mean: there is a handler: it just rethrows
the exception. This is not very interesting of course, but it's
still a handler. Or do you mean something different when you talk
of a handler?
I'm following the notation in comments in the unwinding code
(gcc/unwind*, especially unwind.inc, and libstdc++/libsupc++/eh*,
especially eh_personality.cc). "handler" is used consistently to mean
something that actually handles the thrown exception, a catch-block
for that thrown type. Code that just calls destructors and resumes is
a "cleanup", and is handled differently in the tables and code.
Linux's unwinder uses the same code, doesn't it? I bet if you make the same
change in uw_identify_context that we have you would have the same problem.
and caches where it thinks it
is. That code (cleanup or whatever) is executed and ends by reaching
Unwind_Resume, which tries to resume at where it thinks the handler
is.
I also don't understand this: why is Unwind_Resume looking at where
the exception's handler is?
I don't know the 'why' of any of this; I didn't write it, nor did anybody at Apple.
The handler used for the caught exception
should be irrelevant to it. It sounds like the unwinder thinks that
the Unwind_Resume is in the same eh region as the original throwing
call.
Yes, exactly, and when you're using CFAs rather than IPs to identify it, it is.
The code in question is in Unwind_RaiseException_Phase2, reached from
Unwind_Resume:
/* Identify when we've reached the designated handler context. */
match_handler = (uw_identify_context (context) == exc->private_2
? _UA_HANDLER_FRAME : 0);
Which is back at the same code from which we reached
Unwind_Resume. This worked OK when we were caching the IP because
the IP of the throw and Unwind_Resume call were different, but when
caching the CFA they are the same, which confuses Unwind_Resume.
How is catch-all then rethrow supposed to work then?
It isn't. Catch-all (corresponding to "catch(...)" ) ends the unwinding.
Are you sure
that this isn't simply a bug in where labels are placed etc, the
frame info or the exception table setup etc?
Yes, comparing gcc and llvm output has made me quite sure of that.
(This is darwin's unwinder but I think the patch I quoted is the only
relevant change to generic gcc code.
Could be wrong.)
- lang_eh_catch_all = return_null_tree;
+/* lang_eh_catch_all = return_null_tree;*/
Ok, cool
Duncan, Anton, what do you guys think about this?
This is wrong - it breaks the semantics of invoke and causes
eh to not work properly in some cases (see my email to Dale).
Well, it works a lot better on Darwin than what's there now. I
believe telling the unwinder there is a cleanup is sufficient to get
control always transferred to the secondary label on the invoke.
Are you saying that the Darwin unwinder doesn't have the special
cleanup logic? If so you could indeed push a cleanup rather than
a catch-all on Darwin.
What "special cleanup logic"?