Specifically I'd like to know what, if any, guarantees are made by GCC about how code that throws exceptions behaves when linked against code compiled using -fno-exceptions
.
The GNU libstdc++
manual says the following here.
Before detailing the library support for
-fno-exceptions
, first a passing note on the things lost when this flag is used: it will break exceptions trying to pass through code compiled with-fno-exceptions
whether or not that code has anytry
orcatch
constructs. If you might have some code that throws, you shouldn't use-fno-exceptions
. If you have some code that usestry
orcatch
, you shouldn't use-fno-exceptions
.
This sounds like a statement along the lines of, "Thou shalt not...." I.e. undefined behavior.
On the other hand, my impression from this SO question is that everything is kosher as long as the code compiled with -fno-exceptions
doesn't throw
, try
, or catch
(obviously a compile-time error) and exceptions never propagate through the functions from this library. And it makes sense: why should the library compiled with -fno-exceptions
care if exceptions are thrown so long as they don't interact with its functions?
I did a little tinkering and found that if I compile a simple program using GCC 7.1.1 in which one source file is compiled with -fno-exceptions
and the other throws and catches an exception, everything compiles, links, and runs fine. But that doesn't mean this behavior is guaranteed; it could still be undefined.
My motivation in all of this is that I have a situation in which I'm linking my own application code against a library built with -fno-exceptions
and, depending on which function calls are made to said library, throwing an exception in my own code causes an immediate segfault even when that exception does not propagate through the library's functions. It smells like a bug in the library to me, but I thought maybe this was permitted when -fno-exceptions
was passed during compilation.
GCC's actual reference on code-generation flags mentions -fexceptions
relatively briefly and doesn't answer my question. Anyone know of another reference/have relevant experience?
Update: I rebuilt the library from source, this time with exception support turned on. The segfault persists! Time for a bug report.
For try-with-resources, if an exception is thrown in a try block and in a try-with-resources statement, then the method returns the exception thrown in the try block. The exceptions thrown by try-with-resources are suppressed, i.e. we can say that try-with-resources block throws suppressed exceptions.
What happens if an exception is not caught? If an exception is not caught (with a catch block), the runtime system will abort the program (i.e. crash) and an exception message will print to the console. The message typically includes: name of exception type.
If there's an exception, the code in the corresponding “except” block will run, and then the code in the “finally” block will run. If there are no exceptions, the code in the “else” block will run (if there's an “else” block), and then the code in the “finally” block will run.
If there is no catch block at the current scope matching the thrown exception, the current scope is exited, and all automatic (local nonstatic) objects defined in that scope are destroyed. The surrounding scope (which might be function scope) is checked for a matching handler.
As the linked question points out, GCC needs to allow -fno-exceptions
and -fexceptions
to co-exist, in order to link C and C++.
On a more theoretical level, the exception problem is closely related to the call graph of a program. This is a directed graph (caller/callee) but it can be cyclic, and there can be multiple edges between nodes. Now each function/node can be compiled with or without exceptions. We can define a safe program as a program in which no "with exceptions" node is reachable from a "without exceptions" node.
This may be unnecessarily strict - it might appear reasonable that a C++ bit of code within a try...catch(...) { }
block should be callable from C code. But I don't know of a GCC guarantee for that. And consider what it means - the call graph is related to the call stack. The call stack generally forms a path from main()
to the currently executing function. If the whole path is exception-aware, then exceptions are safe. But if there's one function which isn't aware of exceptions, it might have put the stack in a state in which exceptions cannot be handled safely, even if stack unwinding wouldn't unwind that far.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With