Does C++ exception framework require kernel support?

Viewed 193

I am reading about exception support in C++, probably the result applies to other languages as well.

https://wiki.osdev.org/C%2B%2B_Exception_Support

http://www.ucw.cz/~hubicka/papers/abi/node25.html

There is lots of stuff going on, I am a bit overwhelmed. My question is during the unwinding process, does C++ require help from kernel (e.g. setjmp/longjmp as mentioned in comments, these two functions don't involve kernel, however my question remains the same)? Or the entire process happen only in user space?

I could understand the unwinding of a local thread (think setjmp/longjmp) but is unable to grab how unwinding is done for remote thread.

If all the unwinding magic happens in user space, how can one thread modifies another faulty thread to send it over to handling code without kernel help? Since I believe the thread context information (task struct) lives inside kernel and disallows modification directly from user space, without access to thread context, how is it achieved? Thank you!


Update: It looks like I have some fundamental misunderstanding about the task struct. Just bumped into a slide about signal handling (signal handlers) (http://users.cms.caltech.edu/~donnie/cs124/lectures/CS124Lec15.pdf), upon entering a signal handler, the kernel stack is empty, all context information lives on user stack. I suppose one can use signal to communicate with other faulty thread to unwind? If that's the case, does it mean C++ exception support requires kernel help since generating signal is a kernel call?


Update: As far as the question goes, I am satisfied by Paul's answer. I would like to summarize my understanding on the topic in a broader sense, based on my reading on the comment/answer, please correct me if I am wrong.

C++ exception framework sdoesn't specify particular implementation method, below summary is what I would think it's "common".

There are three sources of exception object, the resulting unwinding is implemented as below:

+------------------------------------------------+--------------------------------------------------------------------------+
|                exception source                |                             unwinding method                             |
+------------------------------------------------+--------------------------------------------------------------------------+
| a) exception created by `throw`                | via code to walk the stack and assembly to transfer control to call site |
| b) exception created by hardware e.g. SIGFPE   | via synchronous signal, and code to walk the stack                       |
|                                                | and assembly to transfer control to call site                            |
| c)* exception created by remote process/thread | via asynchronous signal, and code to walk the stack                      |
|                                                | and assembly to transfer control to call site                            |
+------------------------------------------------+--------------------------------------------------------------------------+

*For case c), I am not sure if there is any real-world usage that requires an exception to be created by a foreign process and handled locally, it doesn't fit the throw/catch semantic either (at least it's doable), I put it here for completeness only.

1 Answers

No.

AFAIK this is all implemented in the C++ runtime library (for GCC this would be libgcc_s.so, at least on Linux). This requires code to walk the stack to look for catch blocks and some machine code to jump into such blocks if found. As already mentioned, setjmp/longjmp also do not require the kernel.

Update: don't mix up C++ exceptions and signals. If you'd like to see more of how C++ exceptions work a good start (if you an comfortable with reading assembler) would be to use godbolt to look at the generated assembler for some simple functions that throw and catch exceptions. Beware that Floating Point Exceptions (FPE) really are signals.

One difference between C++ exceptions and both setjmp/longjmp + signals is the control flow. The latter two implement 'resumption semantics' with a kind of 'call' (where context gets saved) and 'return' (where the context is restored). C++ exceptions on the other hand implement 'termination semantics ' and have no way of returning to the site of the throw.

Signals fall into 2 categories: synchronous signals get delivered immediately (like SIGBUS) and asynchronous ones that get delivered on the next scheduler time slice (like SIGINT). Both cases pass via the kernel. There's no real difference from a user perspective.

threads do not mix well with anything else that does stack manipulation (setjmp/longjmp and signals). In particular multithreaded applications can receive asynchronous signals on any thread. To make this easier to manage, it is common practice to use pthread_sigmask to block all signals on all threads except for one, which will be the signal handling thread.

Related