source: doc/theses/andrew_beach_MMath/unwinding.tex @ 1526e86

Last change on this file since 1526e86 was c72ea7a, checked in by Andrew Beach <ajbeach@…>, 4 years ago

ABMM thesis: Lots of context reworking. Took out Peter's formating and put back parts that I understood and used.

  • Property mode set to 100644
File size: 9.3 KB
1\chapter{Unwinding in \CFA}
3Stack unwinding is the process of removing things from the stack. Within
4functions and on function return this is handled directly by the code in the
5function itself as it knows exactly what is on the stack just from the
6current location in the function. Unwinding across stack frames means that it
7is no longer knows exactly what is on the stack or even how much of the stack
8needs to be removed.
10Even this is fairly simple if nothing needs to happen when the stack unwinds.
11Traditional C can unwind the stack by saving and restoring state (with
12\codeC{setjmp} \& \codeC{longjmp}). However many languages define actions that
13have to be taken when something is removed from the stack, such as running
14a variable's destructor or a \codeCFA{try} statement's \codeCFA{finally}
15clause. Handling this requires walking the stack going through each stack
18For exceptions, this means everything from the point the exception is raised
19to the point it is caught, while checking each frame for handlers during the
20stack walk to find out where it should be caught. This is where the most of
21the expense and complexity of exception handling comes from.
23To do all of this we use libunwind, a low level library that provides tools
24for stack walking and stack unwinding. What follows is an overview of all the
25relivant features of libunwind and then how \CFA uses them to implement its
26exception handling.
28\section{libunwind Usage}
30\CFA uses two primary functions in libunwind to create most of its
31exceptional control-flow: \codeC{_Unwind_RaiseException} and
33Their operation is divided into two phases: search and clean-up. The search
34phase -- phase 1 -- is used to scan the stack but not unwinding it. The
35clean-up phase -- phase 2 -- is used for unwinding.
37The raise-exception function uses both phases. It starts by searching for a
38handler, and if found, performs a clean-up phase to unwind the stack to the
39handler. If a handler is not found, control returns allowing the
40exception-handling policy for unhandled exception to be executed. During both
41phases, the raise-exception function searches down the stack, calling each
42function's \emph{personality function}.
44A personality function performs three tasks, although not all have to be
45present. The tasks performed are decided by the actions provided.
46\codeC{_Unwind_Action} is a bitmask of possible actions and an argument of
47this type is passed into the personality function.
49\item\codeC{_UA_SEARCH_PHASE} is passed in search phase and tells the
50personality function to check for handlers. If there is a handler in this
51stack frame, as defined by the language, the personality function should
52return \codeC{_URC_HANDLER_FOUND}. Otherwise it should return
54\item\codeC{_UA_CLEANUP_PHASE} is passed in during the clean-up phase and
55means part or all of the stack frame is removed. The personality function
56should do whatever clean-up the language defines
57(such as running destructors/finalizers) and then generally returns
59\item\codeC{_UA_HANDLER_FRAME} means the personality function must install
60a handler. It is also passed in during the clean-up phase and is in addition
61to the clean-up action. libunwind provides several helpers for the personality
62function here. Once it is done, the personality function must return
65The personality function is given a number of other arguments. Some are for
66compatability and there is the \codeC{struct _Unwind_Context} pointer which
67passed to many helpers to get information about the current stack frame.
69Forced-unwind only performs the clean-up phase. It takes three arguments:
70a pointer to the exception, a pointer to the stop function and a pointer to
71the stop parameter. It does most of the same things as phase two of
72raise-exception but with some extras.
73The first it passes in an extra action to the personality function on each
74stack frame, \codeC{_UA_FORCE_UNWIND}, which means a handler cannot be
77The big change is that forced-unwind calls the stop function. Each time it
78steps into a frame, before calling the personality function, it calls the
79stop function. The stop function receives all the same arguments as the
80personality function will and the stop parameter supplied to forced-unwind.
82The stop function is called one more time at the end of the stack after all
83stack frames have been removed. By the standard API this is marked by setting
84the stack pointer inside the context passed to the stop function. However both
85GCC and Clang add an extra action for this case \codeC{_UA_END_OF_STACK}.
87Each time function the stop function is called it can do one or two things.
88When it is not the end of the stack it can return \codeC{_URC_NO_REASON} to
89continue unwinding.
90% Is there a reason that NO_REASON is used instead of CONTINUE_UNWIND?
91Its only other option is to use its own means to transfer control elsewhere
92and never return to its caller. It may always do this and no additional tools
93are provided to do it.
95\section{\CFA Implementation}
97To use libunwind, \CFA provides several wrappers, its own storage,
98personality functions, and a stop function.
100The wrappers perform three tasks: set-up, clean-up and controlling the
101unwinding. The set-up allocates a copy of the \CFA exception into a handler to
102control its lifetime, and stores it in the exception context. Clean-up -- run
103when control exits a catch clause and returns to normal code -- frees the
104exception copy.
105% It however does not set up the unwind exception so we can't use any inter-
106% runtime/language features. Also the exception context is global.
108The core control code is called every time a throw -- after set-up -- or
109re-throw is run. It uses raise-exception to search for a handler and to run it
110if one is found. If no handler is found and raise-exception returns then
111forced-unwind is called to run all destructors on the stack before terminating
112the process.
114The stop function is very simple. It checks the end of stack flag to see if
115it is finished unwinding. If so, it calls \codeC{exit} to end the process,
116otherwise it returns with no-reason to continue unwinding.
117% Yeah, this is going to have to change.
119The personality routine is more complex because it has to obtain information
120about the function by scanning the LSDA (Language Specific Data Area). This
121step allows a single personality function to be used for multiple functions and
122let that personaliity function figure out exactly where in the function
123execution was, what is currently in the stack frame and what handlers should
124be checked.
125% Not that we do that yet.
127However, generating the LSDA is difficult. It requires knowledge about the
128location of the instruction pointer and stack layout, which varies with
129compiler and optimization levels. So for frames where there are only
130destructors, GCC's attribute cleanup with the \texttt{-fexception} flag is
131sufficient to handle unwinding.
133The only functions that require more than that are those that contain
134\codeCFA{try} statements. A \codeCFA{try} statement has a \codeCFA{try}
135clause, some number of \codeCFA{catch} clauses and \codeCFA{catchResume}
136clauses and may have a \codeCFA{finally} clause. Of these only \codeCFA{try}
137statements with \codeCFA{catch} clauses need to be transformed and only they
138and the \codeCFA{try} clause are involved.
140The \codeCFA{try} statement is converted into a series of closures which can
141access other parts of the function according to scoping rules but can be
142passed around. The \codeCFA{try} clause is converted into the try functions,
143almost entirely unchanged. The \codeCFA{catch} clauses are converted into two
144functions; the match function and the catch function.
146Together the match function and the catch function form the code that runs
147when an exception passes out of a try block. The match function is used during
148the search phase, it is passed an exception and checks each handler to see if
149it will handle the exception. It returns an index that repersents which
150handler matched or that none of them did. The catch function is used during
151the clean-up phase, it is passed an exception and the index of a handler. It
152casts the exception to the exception type declared in that handler and then
153runs the handler's body.
155These three functions are passed to \codeC{try_terminate}. This is an
156% Maybe I shouldn't quote that, it isn't its actual name.
157internal hand-written function that has its own personality function and
158custom assembly LSD does the exception handling in \CFA. During normal
159execution all this function does is call the try function and then return.
160It is only when exceptions are thrown that anything interesting happens.
162During the search phase the personality function gets the pointer to the match
163function and calls it. If the match function returns a handler index the
164personality function saves it and reports that the handler has been found,
165otherwise unwinding continues.
166During the clean-up phase the personality function only does anything if the
167handler was found in this frame. If it was then the personality function
168installs the handler, which is setting the instruction pointer in
169\codeC{try_terminate} to an otherwise unused section that calls the catch
170function, passing it the current exception and handler index.
171\codeC{try_terminate} returns as soon as the catch function returns.
173At this point control has returned to normal control flow.
Note: See TracBrowser for help on using the repository browser.