[cfe-dev] Unwind, exception handling, debuggers and profilers

Logan Chien tzuhsiang.chien at gmail.com
Fri Mar 21 11:47:47 PDT 2014


Hi,

I wrote that article because I encountered an issue[1] when I was
throwing-and-catching in a C++ program (which is not related to the -g,
-pg, or -Og.)  I am not familiar with the debug_info issue, thus I have no
comments on for the impact to the unwind table and debugging information.

I think we can focus on this:

We would like to add a flag (or reusing -fno-unwind-tables) to Clang so
that the user can disable the generation of unwind table for each function.

To address this issue, we have to consider several aspects:

1. What does the meaning of -funwind-tables?

2. What LLVM assembly will be generated by Clang?  We have to compare the
difference between C and C++ program.

3. What are the meanings of nounwind and uwtable attribute?  What do they
guarantee?  How do they work?

4. Possible solution.

Let's discuss these aspects one-by-one.


1. Meaning of -funwind-tables
-----------------------------

According to the `clang --help`, the -funwind-tables will generate the
unwind table even if the function does not need the exception handling
mechanism.  I have checked GCC manual as well.  It says something similar.

However, there is no precise definition to the unwind table.  There is no
guarantee on what the unwind table should contain.  But from the empirical
experience, the unwind table should contain sufficient information for
unwinder to pass through this frame.  Otherwise, several unwinder
implementation such as _Unwind_Backtrace() might not work properly (either
stop unwinding much earlier, terminate the program with abort(), or loops
forever.)

Currently, this flag will be turned on by default when the
clang::ToolChain::IsUnwindTablesDefault() returns true, and the
-fno-asynchronous-unwind-tables are not specified.  For now,
IsUnwindTablesDefault() only return true for x86_64 target.


2. What LLVM assembly will be generated?
----------------------------------------

When compiling following program with `clang -S -emit-llvm test.c` (in C
mode):

    extern void may_throw_exception();
    void test1() { }
    void test2() { may_throw_exception(); }

* Generated function attributes for x86_64:

test1(): nounwind, uwtable
test2(): nounwind, uwtable

* Genearted function attributes for arm:

test1(): nounwind
test2(): nounwind

When compiling the same program with `clang -S -emit-llvm test.cpp` (in C++
mode):

* Generated function attributes for x86_64:

test1(): nounwind, uwtable
test2(): uwtable

* Generated function attributes for arm:

test1(): nounwind
test2(): (neither nounwind nor uwtable)


3. Meaning of `nounwind` and `uwtable` attribute?
-------------------------------------------------

Given the generated LLVM function attributes, what will happen?

* There's the table for x86_64 target:

- no attribute => emit CFI directives
- with nounwind => no CFI directives
- with uwtable => emit CFI directives
- with uwtable+nounwind => emit CFI directives

* There's the table for ARM target:

- no attribute => emit unwind table
- with nounwind => emit unwind table with cantunwind
- with uwtable => emit unwind table
- with uwtable+nounwind => emit unwind table WITHOUT the cantunwind

The cantunwind record will stop the unwinder, and cantunwind will conflict
with the stack unwind information according to EHABI.  Thus, we should not
emit cantunwind for the function with uwtable.

Please recall that in the C programming language mode, Clang will add
nounwind attribute to function definitions, thus we have to specify
-funwind-tables in order to support the interleaving of C and C++ programs.


4. Possible Solution
--------------------

Back to the original issue, we would like to provide an option to the user
so that the user can reduce the excutable size by omitting the unwind table
(at their own risk.)  In the first attempt, we might use the uwtable
attribute to determine whether we can emit the unwind table or not.  But
according to the comparison in section 3, we can't do so because

a) The function with no attribute will no have unwind table anymore.  This
won't be acceptable unless every targets are enabling -funwind-tables by
default.

b) The function with nounwind attribute can't be properly encoded for ARM.
The implementation for nounwind is slightly different with
uwtable+nounwind.  In the nounwind case, the function will be marked with
cantunwind, and _Unwind_RaiseException() will stop unwinding and
__cxa_throw() will call std::terminate().  In the uwtable+nounwind case, if
the exception occurs, then the exception might be caught by the caller.
Although this difference might not be a problem because (i) throwing from a
function with nounwind is actually an undefined behavior and (ii) clang
will emit a landing pad as a safety net.

IMO, maybe we can add a new "no-unwind-table" attribute to LLVM IR.  If
"no-unwind-table" attribute exists, then we should not emit any unwind
table (including the CFI directives.)  If the clang users explicitly
specified -fno-unwind-tables, then Clang will add "no-unwind-table" to
every function definitions in the translation unit.

Best regards,
Logan

# Footnotes
[1] This issue has been worked around in r202165.


On Thu, Mar 20, 2014 at 5:52 PM, Renato Golin <renato.golin at linaro.org>wrote:

> On 20 March 2014 02:09, Rafael EspĂ­ndola <rafael.espindola at gmail.com>
> wrote:
> > I think this is just 2. It uses .eh_frame for unwinding proper. The
> > only  difference in .eh_frame is that there is a personality function
> > defined.
>
> If there is no debug information, it should still be possible to
> unwind the stack via the saved LR on the stack, no?
>
> If there is only line info, you could even print the function names
> with just the LR.
>
> But yes, Debug and EH unwinding should be identical (modulo the PR).
>
>
> > No. The -g option should never change the set of optimizations that
> > are run.
>
> Bad wording. I meant fix the edge cases Logan reported on the LR
> removal, which might have some effect (bigger frames by one word), but
> that's discussion for another thread.
>
>
> >> * uwtable
> >>   - Generated only when -g or -pg are specified
> >
> > No. Se above note about -g.
>
> I don't see how not having unwinding information would change the
> binary execution.
>
>
>
> > What we do today then is that on x86-64 "clang -S" adds uwtable to all
> > functions and "clang -S -fno-asynchronous-unwind-tables" doesn't.
>
> This is remarkably similar to the behaviour I want to create. But that
> can't be encoded in IR right now wrt. nothrow.
>
> These are the options:
>
> 1. no attr: don't emit tables
> 2. nounwind: emit CantUnwind
> 3. uwtable: emit table
> 4. uwtable + nounwind: emit table
>
> This is because uwtable means *also* debug/profiler use, and emitting
> CantUnwind could stop them from unwinding, since there is no
> information on how to continue unwinding the stack.
>
> The semantics I want is to be able to separate between EH unwinding
> and Debug/Profiler unwinding (even though they map to the same
> physical tables), so that we DO emit CantUnwind in those cases.
>
>
> > * arm-unwind-table. The other unwind table format.
>
> No. This is not an ARM specific issue. The table format is back-end
> specific, and the IR has no business in interfering with it.
>
> cheers,
> --renato
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20140322/471c62ba/attachment.html>


More information about the cfe-dev mailing list