| 1 | \chapter{Unwinding in \CFA}
|
|---|
| 2 |
|
|---|
| 3 | When a function returns, a \emph{single} stack frame is unwound, removing the
|
|---|
| 4 | function's parameters and local variables, and control continues in the
|
|---|
| 5 | function caller using the caller's stack frame. When an exception is raised,
|
|---|
| 6 | \emph{multiple} stack frames are unwound, removing the function parameters and
|
|---|
| 7 | local variables for called functions from the exception raise-frame to the
|
|---|
| 8 | exception catch-frame.
|
|---|
| 9 |
|
|---|
| 10 | Unwinding multiple levels is simple for a programming languages without object
|
|---|
| 11 | destructors or block finalizers because a direct transfer is possible from the
|
|---|
| 12 | current stack frame to a prior stack frame, where control continues at a
|
|---|
| 13 | location within the prior caller's function. For example, C provides non-local
|
|---|
| 14 | transfer using $longjmp$, which stores a function's state including its
|
|---|
| 15 | frame pointer and program counter, and simply reloads this information to
|
|---|
| 16 | continue at this prior location on the stack.
|
|---|
| 17 |
|
|---|
| 18 | For programming languages with object destructors or block finalizers it is
|
|---|
| 19 | necessary to walk the stack frames from raise to catch, checking for code that
|
|---|
| 20 | must be executed as part of terminating each frame. Walking the stack has a
|
|---|
| 21 | higher cost, and necessary information must available to detect
|
|---|
| 22 | destructors/finalizers and call them.
|
|---|
| 23 |
|
|---|
| 24 | A powerful package to provide stack-walking capabilities is $libunwind$,
|
|---|
| 25 | which is used in this work to provide exception handling in \CFA. The following
|
|---|
| 26 | explains how $libunwind$ works and how it is used.
|
|---|
| 27 |
|
|---|
| 28 | % Stack unwinding is the process of removing things from the stack from outside
|
|---|
| 29 | % the functions there. In languages that don't provide a way to guaranty that
|
|---|
| 30 | % code will run when the program leaves a scope or finishes a function, this
|
|---|
| 31 | % can be relatively trivial. C does this with $longjmp$ by setting the
|
|---|
| 32 | % stack pointer and a few other registers.
|
|---|
| 33 |
|
|---|
| 34 | \section{libunwind Usage}
|
|---|
| 35 |
|
|---|
| 36 | There are two primary functions that \CFA uses in $libunwind$ to create most of
|
|---|
| 37 | the exceptional control-flow. These functions are
|
|---|
| 38 | $_Unwind_RaiseException$ and $_Unwind_ForcedUnwind$.
|
|---|
| 39 |
|
|---|
| 40 | Their operation is divided into two phases: search and clean-up. The search
|
|---|
| 41 | phase -- phase 1 -- is used to scan the stack but not unwinding it. The
|
|---|
| 42 | clean-up phase -- phase 2 -- is used to during unwinding.
|
|---|
| 43 |
|
|---|
| 44 | % Somewhere around here I need to talk about the control structures.
|
|---|
| 45 | % $_Unwind_Exception$ is used to carry the API's universal data. Some
|
|---|
| 46 | % of this is internal, other fields are used to communicate between different
|
|---|
| 47 | % exception handling mechanisms in different runtimes.
|
|---|
| 48 | % $_Unwind_Context$ is an opaque data structure that is used to pass
|
|---|
| 49 | % information to helper functions.
|
|---|
| 50 |
|
|---|
| 51 | The raise-exception function uses both phases. It starts by searching for the
|
|---|
| 52 | handler, and if found, performs a clean-up phase to unwind the stack to the
|
|---|
| 53 | hander. If a handler is not found, control returns allowing the
|
|---|
| 54 | exception-handling policy for unhandled exception to be executed. During both
|
|---|
| 55 | phases, the raise exception function searches down the stack, calling each
|
|---|
| 56 | function's \emph{personality function}.
|
|---|
| 57 |
|
|---|
| 58 | A personality function performs three tasks, although not all of them have to
|
|---|
| 59 | be present. The tasks performed are decided by the actions provided.
|
|---|
| 60 | % Something argument something bitmask.
|
|---|
| 61 | \begin{itemize}
|
|---|
| 62 | \item$_UA_SEARCH_PHASE$ is called during the clean-up phase and means search
|
|---|
| 63 | for handlers. If the hander is found, the personality function should return
|
|---|
| 64 | $_URC_HANDLER_FOUND$, otherwise it returns $_URC_CONTINUE_UNWIND$.
|
|---|
| 65 | \item$_UA_CLEANUP_PHASE$ is passed in during the clean-up phase and means part
|
|---|
| 66 | or all of the stack frame is removed. The personality function should do
|
|---|
| 67 | whatever clean-up the language defines (such as running destructors/finalizers)
|
|---|
| 68 | and then generally returns $_URC_CONTINUE_UNWIND$.
|
|---|
| 69 | \item$_UA_HANDLER_FRAME$ means the personality function must install a
|
|---|
| 70 | handler. It is also passed in during the clean-up phase and is in addition to
|
|---|
| 71 | the clean-up action. Libunwind provides several helpers for the personality
|
|---|
| 72 | function here. Once it is done, the personality function must return
|
|---|
| 73 | $_URC_INSTALL_CONTEXT$.
|
|---|
| 74 | \end{itemize}
|
|---|
| 75 |
|
|---|
| 76 | Forced unwind only performs the clean-up phase. It is similar to the phase 2
|
|---|
| 77 | section of raise exception with a few changes. A simple one is that it passes
|
|---|
| 78 | in an extra action to the personality function $_UA_FORCE_UNWIND$, which means
|
|---|
| 79 | a handler cannot be installed. The most significant is the addition of the $stop$
|
|---|
| 80 | function, which is passed in as an argument to the forced unwind.
|
|---|
| 81 |
|
|---|
| 82 | The stop function is a lot like a personality function. It takes an extra
|
|---|
| 83 | argument, a void pointer passed into force unwind. It may return
|
|---|
| 84 | $_URC_NO_REASON$ to continue unwinding or it can transfer control out of the
|
|---|
| 85 | unwind code using its own mechanism.
|
|---|
| 86 | % Is there a reason that NO_REASON is used instead of CONTINUE_UNWIND?
|
|---|
| 87 | The stop function is called once on every stack frame and once at the end of
|
|---|
| 88 | the stack. In a stack frame, it is called before the personality routine with
|
|---|
| 89 | the same arguments (except for the extra void pointer). At the end of the
|
|---|
| 90 | stack, the arguments are mostly the same, except the stack pointer stored in
|
|---|
| 91 | the context is set to null. Because of the significance of that change, both
|
|---|
| 92 | GCC and Clang add an extra action in this case $_UA_END_OF_STACK$. The stop
|
|---|
| 93 | function may not return at the end of the stack.
|
|---|
| 94 |
|
|---|
| 95 | \section{\CFA Implementation}
|
|---|
| 96 |
|
|---|
| 97 | To use $libunwind$, \CFA provides several wrappers, its own storage, the
|
|---|
| 98 | personality functions, and a stop function.
|
|---|
| 99 |
|
|---|
| 100 | The wrappers perform three tasks: set-up, clean-up and controlling the
|
|---|
| 101 | unwinding. The set-up allocates a copy of the \CFA exception into a handler
|
|---|
| 102 | so it can control its lifetime, and stores it in the exception context.
|
|---|
| 103 | Clean-up -- run when control exits a catch clause and return to normal code --
|
|---|
| 104 | frees the exception 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.
|
|---|
| 107 |
|
|---|
| 108 | The control code in the middle is run every time a throw or re-throw is
|
|---|
| 109 | called. It uses raise exception to search for a handler and to run it if one
|
|---|
| 110 | is found. Otherwise it uses forced unwind to unwind the stack, running all
|
|---|
| 111 | destructors, before terminating the process.
|
|---|
| 112 |
|
|---|
| 113 | The stop function is very simple, it checks the end of stack flag to see if it
|
|---|
| 114 | is finished unwinding. If so, it calls exit to end the process, otherwise it
|
|---|
| 115 | tells the system to continue unwinding.
|
|---|
| 116 | % Yeah, this is going to have to change.
|
|---|
| 117 |
|
|---|
| 118 | The personality routine is much more complicated. First, because it has to get
|
|---|
| 119 | some information about the function by scanning the LSDA (Language Specific
|
|---|
| 120 | Data Area). This allows a single personality function to be used for multiple
|
|---|
| 121 | functions and it accounts for multiple regions and possible handlers in a
|
|---|
| 122 | single function.
|
|---|
| 123 | % Not that we do that yet.
|
|---|
| 124 |
|
|---|
| 125 | However generating the LSDA is very difficult. It requires a lot of knowledge
|
|---|
| 126 | about the location of the instruction pointer and stack layout that can be
|
|---|
| 127 | broken by optimization. So instead for frames where there are only destructors
|
|---|
| 128 | we use GCC's attribute cleanup and the -fexception flag to handle unwinding
|
|---|
| 129 | without adding our own functionality.
|
|---|
| 130 |
|
|---|
| 131 | For functions that do have handlers (defined in try statements) the function
|
|---|
| 132 | is split into several functions. Everything outside the try statement is the
|
|---|
| 133 | first function, which has then has only destructors to be run during
|
|---|
| 134 | unwinding. The clauses of the try block are then converted into GCC inner
|
|---|
| 135 | functions which can be passed around with function pointers while still having
|
|---|
| 136 | access to the outer function's scope. $catchResume$ and $finally$
|
|---|
| 137 | clauses are handled separately and will not be discussed here.
|
|---|
| 138 |
|
|---|
| 139 | The $try$ clause is converted to a function directly. The $catch$
|
|---|
| 140 | clauses are combined and then create two functions. The first is the match
|
|---|
| 141 | function which is used during the search phase to see if any of the handler
|
|---|
| 142 | here. The second it the catch function, which is a large switch-case block
|
|---|
| 143 | with the different handlers. All of these function do not interact with
|
|---|
| 144 | unwinding except for running destructors and so can be handled by GCC.
|
|---|
| 145 |
|
|---|
| 146 | These three functions are passed into $try_terminate$, an internal
|
|---|
| 147 | function that represents the try statement. This is the only function with
|
|---|
| 148 | our personality function as well as assembly statements that create the LSDA.
|
|---|
| 149 | In normal execution all the function does is call the try block closure and
|
|---|
| 150 | return once that has finished executing. However using $libunwind$ its
|
|---|
| 151 | personality function now handles exception matching and catching.
|
|---|
| 152 |
|
|---|
| 153 | During the search phase the personality function retrieves the match function
|
|---|
| 154 | from the stack using the saved stack pointer. The function is called, either
|
|---|
| 155 | returning 0 for no match or the index (a positive integer) of the handler
|
|---|
| 156 | that will handle it. The if a handler was found the personality function
|
|---|
| 157 | reports it after saving the index to the exception context.
|
|---|
| 158 |
|
|---|
| 159 | During the clean-up phase there is nothing for the personality function to
|
|---|
| 160 | clean-up in $try_terminate$. So if this is not the handler frame
|
|---|
| 161 | unwinding continues. If this is the handler frame than control is transferred
|
|---|
| 162 | to the catch function, giving it the exception and the handler index.
|
|---|