Tuesday, 30 April 2013
Tip to migrate from VMWare to VirtualBox
Here's a little tip for those times when you are trying to get the network for a VMW image working in VBox: try using the third network adapter, that seems to be the same PCI address VMWare uses (in my machine... results might vary).
Thursday, 25 April 2013
C++ exceptions under the hood 13: setting the context for a landing pad
Last time we finally wrote an almost working personality function. We can detect for each stack frame which landing pads are available and then tell _Unwind_ we want to run a specific landing pad. We hit a small issue, though: although we set the context for _Unwind_ to continue executing on the correct landing pad we didn't set the current exception on the register. This, in turn, means that the landing pad won't know which exception should be handling, so it will say "I can't handle this". _Unwind_ will then say "please try the next landing pad" but our ABI is so simple that it has no idea how it should find another landing pad and just tries the same. Over and over again. We have probably invented the most contrived example for a while(true)!
Let's set the correct context for the landing pad and clean up a bit our ABI:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
namespace __cxxabiv1 {
struct __class_type_info {
virtual void foo() {}
} ti;
}
#define EXCEPTION_BUFF_SIZE 255
char exception_buff[EXCEPTION_BUFF_SIZE];
extern "C" {
void* __cxa_allocate_exception(size_t thrown_size)
{
printf("alloc ex %i\n", thrown_size);
if (thrown_size > EXCEPTION_BUFF_SIZE) printf("Exception too big");
return &exception_buff;
}
void __cxa_free_exception(void *thrown_exception);
#include <unwind.h>
typedef void (*unexpected_handler)(void);
typedef void (*terminate_handler)(void);
struct __cxa_exception {
std::type_info * exceptionType;
void (*exceptionDestructor) (void *);
unexpected_handler unexpectedHandler;
terminate_handler terminateHandler;
__cxa_exception * nextException;
int handlerCount;
int handlerSwitchValue;
const char * actionRecord;
const char * languageSpecificData;
void * catchTemp;
void * adjustedPtr;
_Unwind_Exception unwindHeader;
};
void __cxa_throw(void* thrown_exception,
struct type_info *tinfo,
void (*dest)(void*))
{
printf("__cxa_throw called\n");
__cxa_exception *header = ((__cxa_exception *) thrown_exception - 1);
_Unwind_RaiseException(&header->unwindHeader);
// __cxa_throw never returns
printf("no one handled __cxa_throw, terminate!\n");
exit(0);
}
void __cxa_begin_catch()
{
printf("begin FTW\n");
}
void __cxa_end_catch()
{
printf("end FTW\n");
}
/***********************************************************************/
/**
* The LSDA is a read only place in memory; we'll create a typedef for
* this to avoid a const mess later on; LSDA_ptr refers to readonly and
* &LSDA_ptr will be a non-const pointer to a const place in memory
*/
typedef const uint8_t* LSDA_ptr;
struct LSDA_Header {
/**
* Read the LSDA table into a struct; advances the lsda pointer
* as many bytes as read
*/
LSDA_Header(LSDA_ptr *lsda) {
LSDA_ptr read_ptr = *lsda;
// Copy the LSDA fields
start_encoding = read_ptr[0];
type_encoding = read_ptr[1];
ttype = read_ptr[2];
// Advance the lsda pointer
*lsda = read_ptr + sizeof(LSDA_Header);
}
uint8_t start_encoding;
uint8_t type_encoding;
uint8_t ttype;
};
struct LSDA_CS_Header {
// Same as other LSDA constructors
LSDA_CS_Header(LSDA_ptr *lsda) {
LSDA_ptr read_ptr = *lsda;
encoding = read_ptr[0];
length = read_ptr[1];
*lsda = read_ptr + sizeof(LSDA_CS_Header);
}
uint8_t encoding;
uint8_t length;
};
struct LSDA_CS {
// Same as other LSDA constructors
LSDA_CS(LSDA_ptr *lsda) {
LSDA_ptr read_ptr = *lsda;
start = read_ptr[0];
len = read_ptr[1];
lp = read_ptr[2];
action = read_ptr[3];
*lsda = read_ptr + sizeof(LSDA_CS);
}
// Note start, len and lp would be void*'s, but they are actually relative
// addresses: start and lp are relative to the start of the function, len
// is relative to start
// Offset into function from which we could handle a throw
uint8_t start;
// Length of the block that might throw
uint8_t len;
// Landing pad
uint8_t lp;
// Offset into action table + 1 (0 means no action)
// Used to run destructors
uint8_t action;
};
/*********************************************************************/
_Unwind_Reason_Code __gxx_personality_v0 (
int version,
_Unwind_Action actions,
uint64_t exceptionClass,
_Unwind_Exception* unwind_exception,
_Unwind_Context* context)
{
if (actions & _UA_SEARCH_PHASE)
{
printf("Personality function, lookup phase\n");
return _URC_HANDLER_FOUND;
} else if (actions & _UA_CLEANUP_PHASE) {
printf("Personality function, cleanup\n");
// Pointer to the beginning of the raw LSDA
LSDA_ptr lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context);
// Read LSDA headerfor the LSDA
LSDA_Header header(&lsda);
// Read the LSDA CS header
LSDA_CS_Header cs_header(&lsda);
// Calculate where the end of the LSDA CS table is
const LSDA_ptr lsda_cs_table_end = lsda + cs_header.length;
// Loop through each entry in the CS table
while (lsda < lsda_cs_table_end)
{
LSDA_CS cs(&lsda);
if (cs.lp)
{
int r0 = __builtin_eh_return_data_regno(0);
int r1 = __builtin_eh_return_data_regno(1);
_Unwind_SetGR(context, r0, (uintptr_t)(unwind_exception));
// Note the following code hardcodes the exception type;
// we'll fix that later on
_Unwind_SetGR(context, r1, (uintptr_t)(1));
uintptr_t func_start = _Unwind_GetRegionStart(context);
_Unwind_SetIP(context, func_start + cs.lp);
break;
}
}
return _URC_INSTALL_CONTEXT;
} else {
printf("Personality function, error\n");
return _URC_FATAL_PHASE1_ERROR;
}
}
}
Note: For a more detailed description of the LSDA tables check here and for the full source code check my github repo.
Finally, it worked. You should see something like this if you run it:
./app
alloc ex 1
__cxa_throw called
Personality function, lookup phase
Personality function, cleanup
begin FTW
Caught a Fake_Exception!
end FTW
try_but_dont_catch handled the exception
catchit handled the exception
Of course we are lying a bit to _Unwind_: we are saying here that we will handle every exception, no mater what. This turns our catch(Exception&) into a catch(...), and all hell will break loose if the first function up in the call frame doesn't have a catch statement. But still, we reached the first milestone for a very simple ABI.
Can we now improve it and make it handle the correct exception on the correct frame? May be next time.
Tuesday, 23 April 2013
C++ exceptions under the hood 14: multiple landing pads & the teachings of the guru
After a lot of hard work, last time we finally got a working personality function capable of handling exceptions without help of libstdc++. It will indiscriminately handle all exceptions, but it does work. There is a big question we haven't answered yet: if we go back to the LSDA (language specific data area) we'll see something like this:
.local_lsda_call_site_table:
.uleb128 .LEHB0-.LFB1
.uleb128 .LEHE0-.LEHB0
.uleb128 .L8-.LFB1
.uleb128 0x1
.uleb128 .LEHB1-.LFB1
.uleb128 .LEHE1-.LEHB1
.uleb128 0
.uleb128 0
.uleb128 .LEHB2-.LFB1
.uleb128 .LEHE2-.LEHB2
.uleb128 .L9-.LFB1
.uleb128 0
.local_lsda_call_site_table_end:
There are 3 landing pads defined there, even though we wrote a single try/catch statement. What is going on there?
If you read carefully the last entry on this topic maybe you noticed I added some comments to the definition of struct LSDA_CS:
struct LSDA_CS {
// Note start, len and lp would be void*'s, but they are actually relative
// addresses: start and lp are relative to the start of the function, len
// is relative to start
// Offset into function from which we could handle a throw
uint8_t start;
// Length of the block that might throw
uint8_t len;
// Landing pad
uint8_t lp;
// Offset into action table + 1 (0 means no action)
// Used to run destructors
uint8_t action;
};
Note: You can download the full sourcecode for this project in my github repo.
Something very interesting is going on here, but lets first analyze this struct field by field with the following example:
void foo() {
L0:
try {
do_something();
L1:
} catch (const Exception1& ex) {
...
} catch (const Exception2& ex) {
...
} catch (const ExceptionN& ex) {
...
} catch (...) {
}
L2:
}
- lp: the offset from the start of the function where the landing pad starts. The value of lp for the following example would be L1 - addr_of(foo)
- action: an offset into an action table. This is used to run the cleanup actions while unwinding the stack. We haven't reached this point yet, we can ignore it for now.
- start: the offset from the start of the function where the try block begins: in the example this would be L0 - addr_of(foo)
- len: the length of the try block. On the example this would be L1 - L0
The interesting fields now are start and len: in a function with multiple try/catch blocks we can know whether we should handle an exception by checking if the instruction pointer for the current frame is between start and start + len.
That solves the mystery of how a function with multiple try/catch blocks can handle an exception but we still have another question: why are there three call sites when we only specified a single landing pad? The other three are places where an exception might be thrown so they get added as a possible place for cleanup actions or landing pads. If we learned anything from GOTW it should be that exceptions can be thrown in the places we least expect. There is an entry in the call site table for our throw because it's a block that might throw; the compiler also detected another three.
Now that we know what the start and len fields do, let's change our personality function so the correct landing pad can handle the exception being thrown. Go ahead. My implementation for the next time.
Thursday, 18 April 2013
Learning misspells: thanks Vim!
I wonder how can I fix this problem without a spellchecker for vim.
Tuesday, 16 April 2013
C++ exceptions under the hood 12: and suddenly, reflexion in C++
We left our mini-ABI project (link) capable of throwing exceptions, and we are now working on catching them; we implemented a personality function last time which was capable of detecting and handling exceptions but it was still a bit incomplete: even though it can properly notify the stack unwinder when it should stop but our version of __gxx_personality_v0 can't run the code inside a catch block. We learned last time how to read the LSDA, so now it's only a problem of putting all the pieces together to read the .gcc_except_table from within our personality function.
Let's recap a bit: we figured out last time that our LSDA for the function which has the catch we want to run has the following call site table (that is, the following landing pads [that is, the following catch blocks]):
.local_lsda_call_site_table:
.uleb128 .LEHB0-.LFB1
.uleb128 .LEHE0-.LEHB0
.uleb128 .L8-.LFB1
.uleb128 0x1
.uleb128 .LEHB1-.LFB1
.uleb128 .LEHE1-.LEHB1
.uleb128 0
.uleb128 0
.uleb128 .LEHB2-.LFB1
.uleb128 .LEHE2-.LEHB2
.uleb128 .L9-.LFB1
.uleb128 0
.local_lsda_call_site_table_end:
All those labels can be mapped to different places in the assembly of our function, but it's a bit too messy for a blog post (I do recommend you to disassemble the function yourself and try to match each label, a lot can be learned from doing it). Also, thanks to some random Internet page, we learned the format for this table.
Let's do something like this to see if we're on the right track (beware of read-alignment issues and keep in mind that defining CFI structures like this will only work for uint8's and is probably not portable):
struct LSDA_Header {
uint8_t lsda_start_encoding;
uint8_t lsda_type_encoding;
uint8_t lsda_call_site_table_length;
};
struct LSDA_Call_Site_Header {
uint8_t encoding;
uint8_t length;
};
struct LSDA_Call_Site {
LSDA_Call_Site(const uint8_t *ptr) {
cs_start = ptr[0];
cs_len = ptr[1];
cs_lp = ptr[2];
cs_action = ptr[3];
}
uint8_t cs_start;
uint8_t cs_len;
uint8_t cs_lp;
uint8_t cs_action;
};
_Unwind_Reason_Code __gxx_personality_v0 (
int version, _Unwind_Action actions, uint64_t exceptionClass,
_Unwind_Exception* unwind_exception, _Unwind_Context* context)
{
if (actions & _UA_SEARCH_PHASE)
{
printf("Personality function, lookup phase\n");
return _URC_HANDLER_FOUND;
} else if (actions & _UA_CLEANUP_PHASE) {
printf("Personality function, cleanup\n");
const uint8_t* lsda = (const uint8_t*)
_Unwind_GetLanguageSpecificData(context);
LSDA_Header *header = (LSDA_Header*)(lsda);
LSDA_Call_Site_Header *cs_header = (LSDA_Call_Site_Header*)
(lsda + sizeof(LSDA_Header));
size_t cs_in_table = cs_header->length / sizeof(LSDA_Call_Site);
// We must declare cs_table_base as uint8, otherwise we risk an
// unaligned access
const uint8_t *cs_table_base = lsda + sizeof(LSDA_Header)
+ sizeof(LSDA_Call_Site_Header);
// Go through every entry on the call site table
for (size_t i=0; i < cs_in_table; ++i)
{
const uint8_t *offset = &cs_table_base[i * sizeof(LSDA_Call_Site)];
LSDA_Call_Site cs(offset);
printf("Found a CS:\n");
printf("\tcs_start: %i\n", cs.cs_start);
printf("\tcs_len: %i\n", cs.cs_len);
printf("\tcs_lp: %i\n", cs.cs_lp);
printf("\tcs_action: %i\n", cs.cs_action);
}
uintptr_t ip = _Unwind_GetIP(context);
uintptr_t funcStart = _Unwind_GetRegionStart(context);
uintptr_t ipOffset = ip - funcStart;
return _URC_INSTALL_CONTEXT;
} else {
printf("Personality function, error\n");
return _URC_FATAL_PHASE1_ERROR;
}
}
Note: You can download the full sourcecode for this project in my github repo.
As you can see if you run this code, all entries in the call site table are relative. Relative to what? To the start of function. That means that if we want to get the EIP for a specific landing pad all we have to do is _Unwind_GetRegionStart + LSDA_Call_Site.cs_lp!
We should now be able to solve our exceptional problem: let's try to modify our personality function to run the correct landing pad. We'll now need to use another _Unwind_ function to specify where we want to resume execution: _Unwind_SetIP. Let's change the personality function again to run the first landing pad available, which by inspecting the assembly we already know is the one we want:
...
const uint8_t *cs_table_base = lsda + sizeof(LSDA_Header)
+ sizeof(LSDA_Call_Site_Header);
for (size_t i=0; i < cs_in_table; ++i)
{
const uint8_t *offset = &cs_table_base[i * sizeof(LSDA_Call_Site)];
LSDA_Call_Site cs(offset);
if (cs.cs_lp)
{
uintptr_t func_start = _Unwind_GetRegionStart(context);
_Unwind_SetIP(context, func_start + cs.cs_lp);
break;
}
}
return _URC_INSTALL_CONTEXT;
Try to run it, and watch a beautiful infinite loop. Can you guess what went wrong? The answer on the next article.
Thursday, 11 April 2013
C++ exceptions under the hood 11: reading a CFI table
To properly handle exceptions from within the personality function we've been implementing for our ABI, we need to read the LSDA (language specific data area) to know which call frame (ie which function) can handle which exception, and to know where a landing pad (catch block) can be found). The LSDA table is in CFI format, and we'll see in this chapter how to read it.
Reading the CFI data can be rather straight forward, but there are a few pitfalls we need to consider first. Two, actually:
- There is very little documentation about the .gcc_except_table format (actually, I only found some mails about it) so we'll need to read a lot of source code and disassembles to understand it.
- Although the format itself is not terribly complicated, it uses a LEB encoding that makes reading this table not quite straightforward.
As far as I know most DWARF data is encoded like this, using a LEB format, which seems to be great for confusing programmers and to save code space while encoding arbitrary length ints. Luckily, we can cheat a bit in here: most of the time the LEB encoded numbers will readble with a plain uint8_t, because we won't be dealing with large exception tables or anything like that.
Note: You can download the full sourcecode for this project in my github repo.
Let's start by analyzing the CFI data directly from the disassembly, we'll then see if we can build something to read it on our personality function. I'll rename the labels to make them a bit more human-friendly. The LSDA will have three sections, try to spot them below:
.local_frame_entry:
.globl __gxx_personality_v0
.section .gcc_except_table,"a",@progbits
.align 4
This one is very easy: it's just a header to declare we're going to use __gxx_personality_v0 as a global and to let the linker know we're going to be declaring stuff for the .gcc_except_table section. Moving on:
.local_lsda_1:
# This declares the encoding type. We don't care.
.byte 0xff
# This specifies the landing pads start; if zero, the func's ptr is
# assumed (_Unwind_GetRegionStart)
.byte 0
# Length of the LSDA area: check that LLSDATT1 and LLSDATTD1 point to the
# end and the beginning of the LSDA, respectively
.uleb128 .local_lsda_end - .local_lsda_call_site_table_header
This now has some more info. Those labels are quite obscure but they do follow a pattern. LSDA means language specific data area, the L in front means local, so this is the local (to the translation unit, the .o file) language specific data area number one. Other labels follow similar patterns but I haven't taken the job of figuring them out. We don't really need to, anyway.
.local_lsda_call_site_table_header:
# Encoding of items in the landing pad table. Again, we don't care.
.byte 0x1.
# The length of the call site table (ie the landing pads)
.uleb128 .local_lsda_call_site_table_end - .local_lsda_call_site_table
Another boring header. Moving on:
.local_lsda_call_site_table:
.uleb128 .LEHB0-.LFB1
.uleb128 .LEHE0-.LEHB0
.uleb128 .L8-.LFB1
.uleb128 0x1
.uleb128 .LEHB1-.LFB1
.uleb128 .LEHE1-.LEHB1
.uleb128 0
.uleb128 0
.uleb128 .LEHB2-.LFB1
.uleb128 .LEHE2-.LEHB2
.uleb128 .L9-.LFB1
.uleb128 0
.local_lsda_call_site_table_end:
This is much more interesting, now we're seeing the call site table itself. Somehow, in all these entries, we should be able to find our landing pad. According to some random internet page, the format for each call site entry should be:
struct lsda_call_site_entry {
// Start of the IP range
size_t cs_start;
// Length of the IP range
size_t cs_len;
// Landing pad address
size_t cs_lp;
// Offset into action table
size_t cs_action;
};
So we seem to be on the right track, though we don't know yet why there are 3 call site entries when we only defined a single landing pad. In any case, we can cheat a little: by looking at the disassembly we can deduce that all the values on the CFI will be less than 128 and this means that in LEB encoding they can be read as plain uchars. This makes our CFI reading code so much easier, and we will see how to use it in our personality function next time.
Tuesday, 9 April 2013
C++ exceptions under the hood 10: _Unwind_ and call frame info
We left our mini-ABI project (link) capable of throwing exceptions, and we are now working on catching them; we implemented a personality function last time which was capable of detecting and handling exceptions but it was still a bit incomplete: even though it can properly notify the stack unwinder when it should stop but our version of __gxx_personality_v0 can't run the code inside a catch block. It's better than a coredump one might argue, but still a long way from a useful exception handling ABI. Can we improve it?
How can we tell _Unwind_ where is our landing pad, so we can execute the code inside the catch statement? If we go back to the ABI specification, there are a few context management functions which might help us:
- _Unwind_GetLanguageSpecificData, to get the LSDA for this stack frame. We should be able to find the landing pads and the destructors to run using it.
- _Unwind_GetRegionStart, to get the instruction pointer for the beginning of the function for stack frame currently under analysis by the personality function (that is, the function pointer for the current stack frame).
- _Unwind_GetIP, to get the instruction pointer inside the current stack frame (a pointer to the place where the function call to the next stack frame was done. It should be clearer with the example below).
Note: You can download the full sourcecode for this project in my github repo.
Let's check these functions with gdb. On my machine:
Breakpoint 1, __gxx_personality_v0 (version=1, actions=6, exceptionClass=134515400, unwind_exception=0x804a060, context=0xbffff0f0)
at mycppabi.cpp:77
84 const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(context);
85 uintptr_t ip = _Unwind_GetIP(context) - 1;
86 uintptr_t funcStart = _Unwind_GetRegionStart(context);
87 uintptr_t ipOffset = ip - funcStart;
If we inspect those variables we can see that indeed _Unwind_GetRegionStart points to the current stack frame (try_but_dont_catch) and that _Unwind_GetIP is the IP for the position where the call to the next stack frame was done. The _Unwind_GetRegionStart is pointing us to the place where the exception was first thrown; it's a bit complicated to explain and we'll use that later, not now. Also, we don't see the LSDA here, but we can deduce it's after the function's code since _Unwind_GetLanguageSpecificData points directly after the function's end:
_Unwind_GetIP = (void *) 0x804861d
_Unwind_GetRegionStart = (void *) 0x8048612
_Unwind_GetLanguageSpecificData = (void *) 0x8048e3c
function pointer to try_but_dont_catch = 0x8048612 <try_but_dont_catch()>
(gdb) disassemble /m try_but_dont_catch
Dump of assembler code for function try_but_dont_catch():
10 void try_but_dont_catch() {
[...]
11 try {
12 raise();
0x08048619 <+7>: call 0x80485e8 <raise()>
13 } catch(Fake_Exception&) {
0x08048651 <+63>: call 0x804874a <__cxa_begin_catch()>
0x08048665 <+83>: call 0x804875e <__cxa_end_catch()>
0x0804866a <+88>: jmp 0x804861e <try_but_dont_catch()+12>
14 printf("Caught a Fake_Exception!\n");
0x08048659 <+71>: movl $0x8048971,(%esp)
0x08048660 <+78>: call 0x80484c0 <puts@plt>
15 }
16
17 printf("try_but_dont_catch handled the exception\n");
0x0804861e <+12>: movl $0x8048948,(%esp)
0x08048625 <+19>: call 0x80484c0 <puts@plt>
18 }
0x0804862a <+24>: add $0x24,%esp
With the help of _Unwind_ we are now able to get enough information about the current stack frame to decide whether we can or not handle an exception, an also how should we handle it. One more step is needed before we can detect the landing pad we want: we will need to interpret the CFI (call frame information) at the end of the function. This is part of the DWARF spec, the same gdb uses for debugging purposes, and it's not an easy spec to implement. Like we are doing with our ABI, we'll keep this to the bare minimum.
Thursday, 4 April 2013
Vim tips: my github's vimrc
Now whenever I get a new computer I just clone my repo and get for free my vimrc, my terminator rc and a bunch of useful scripts I regularly use on bash.
Tuesday, 2 April 2013
C++ exceptions under the hood 9: catching our first exception
We finished last chapter on the series about C++ exceptions by adding a personality function that _Unwind_ was able to call and then analyzing the parameters that the personality function receives. Now it's time to begin adding some real behavior to __gxx_personality_v0: when __gxx_personality_v0 is called we should say "yes, this stack frame can indeed handle this exception".
We have been building up to this point quite a bit: the time where we can implement for the first time a personality function capable of detecting when an exception is thrown, and then saying "yes, I will handle this exception". For that we had to learn how the two-phase lookup work, so we can now reimplement our personality function and our throw test file:
#include <stdio.h>
#include "throw.h"
struct Fake_Exception {};
void raise() {
throw Exception();
}
void try_but_dont_catch() {
try {
raise();
} catch(Fake_Exception&) {
printf("Caught a Fake_Exception!\n");
}
printf("try_but_dont_catch handled the exception\n");
}
void catchit() {
try {
try_but_dont_catch();
} catch(Exception&) {
printf("Caught an Exception!\n");
}
printf("catchit handled the exception\n");
}
extern "C" {
void seppuku() {
catchit();
}
}
And our personality function:
_Unwind_Reason_Code __gxx_personality_v0 (
int version, _Unwind_Action actions, uint64_t exceptionClass,
_Unwind_Exception* unwind_exception, _Unwind_Context* context)
{
if (actions & _UA_SEARCH_PHASE)
{
printf("Personality function, lookup phase\n");
return _URC_HANDLER_FOUND;
} else if (actions & _UA_CLEANUP_PHASE) {
printf("Personality function, cleanup\n");
return _URC_INSTALL_CONTEXT;
} else {
printf("Personality function, error\n");
return _URC_FATAL_PHASE1_ERROR;
}
}
Note: You can download the full sourcecode for this project in my github repo.
Let's run it, see what happens:
alloc ex 1
__cxa_throw called
Personality function, lookup phase
Personality function, cleanup
try_but_dont_catch handled the exception
catchit handled the exception
It works, but something is missing: the catch inside the catch/try block is not being executed! This is happening because the personality function tells Unwind to "install a context" (ie to resume execution) but it never says which context. In this case it's probably resuming executing from after the landing pad, but I'd bet this is actually undefined behavior. We'll see next time how we can specify we want to resume executing from a specific landing pad using the information available on .gcc_except_table (our old friend, the LSDA).