﻿# How catch\-block selection works in exception handling

If a pill knows what to treat, could an exception also understand when to stop its journey through the stack? In application programming, a description like this is often enough, but sometimes one wants more details\. Well, let's dive in\!

![1360_LinuxExceptionTwo/image1.png](https://import.viva64.com/docx/blog/1360_LinuxExceptionTwo/image1.png)

[Last time](https://pvs-studio.com/en/blog/posts/cpp/1333/), we looked at how exceptions are thrown and caught, as well as the connection between these two processes\. We've seen functions of the `__cxa*` and `_Unwind*` families, how they intertwine, and how the compiler embeds some of them into assembly code along with additional code blocks when compiling programs\. 

Some questions still remain unanswered\. We've moved on from SjLj exceptions, and we've paused the discussion of zero\-cost exceptions at the most thrilling point: the personality routine\. In this article, we'll pick up right where we left off to answer the question, "How does an exception know it has reached the right `catch` block?"

Just like last time, we'll be using the `libcxx` library from LLVM\. We'll also use the same compiler, Clang 21\.1\.0 for x86\-64\.

## Previously\.\.\.

While exploring the inner workings of the system, we came across two functions, `[_Unwind_RaiseException](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libunwind/src/UnwindLevel1.c#L467)` and `[_Unwind_ForcedUnwind](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindLevel1.c#L525)`, and thoroughly looked into how they work\. The second function, which involved forced stack unwinding, was less exciting since it had no direct connection to our topic\. However, the first one paved the way for understanding how exceptions are delivered through the program stack\. It was executed in two stages: locating the appropriate `catch` block, and then delivering the exception along with calling automatic variable destructors\.

While the stack traversal part is now clear, the process of determining the correct exception handler remains a mystery\. All we know about how it works is that at some point during the execution of both stages, [code like this executes](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libunwind/src/UnwindLevel1.c#L297):

```cpp
_Unwind_Personality_Fn p = get_handler_function(&frameInfo); 
// ....
_Unwind_Reason_Code personalityResult =(*p)(
    1, action, exception_object->exception_class, 
    exception_object, (struct _Unwind_Context *)(cursor)
);
```

In the code, the `frameInfo` variable of the `[unw_proc_info_t](https://github.com/llvm/llvm-project/blob/9e25a423d07bdcc2686defedcd509fb405d250db/libunwind/include/libunwind.h#L193)` type is used to retrieve `handler`, which is a function that determines when to stop stack unwinding\. We can see that calling the `p` function returns some result\. The calling stack unwinding driver uses this result to determine what to do next with the unwinding\.

The `frameInfo` variable contains important information: a pointer to the `handler` itself and several other data members\. They include pointers to the start and end of the function whose stack frame is being evaluated at the current stack unwinding stage, as well as other details\.

In the previous article, we encountered the `__gxx_personality_v0` symbol, which was defined in the assembly code that Clang generated from our test project\. This symbol may be related to the `handler`, but we haven't proven it yet\.

Let's revisit this project's code:

```cpp
int bar()
{
    throw -1;
}

int foo()
{
    try {
        return bar();
    }
    catch (...) {
        return -1;
    }
}

int main()
{
    return foo();
}
```

We'll convert it to [assembly code](https://godbolt.org/z/vhMnrj6a6) and work with that\.

<details>
   <summary>Assembly code</summary>

```cpp
bar():
        push    rbp
        mov     rbp, rsp
        mov     edi, 4
        call    __cxa_allocate_exception@PLT
        mov     rdi, rax
        mov     dword ptr [rdi], -1
        mov     rsi, qword ptr [rip + typeinfo for int@GOTPCREL]
        xor     eax, eax
        mov     edx, eax
        call    __cxa_throw@PLT

foo():
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        call    bar()
        mov     dword ptr [rbp - 16], eax
        jmp     .LBB1_1
.LBB1_1:
        mov     eax, dword ptr [rbp - 16]
        add     rsp, 16
        pop     rbp
        ret
        mov     rcx, rax
        mov     eax, edx
        mov     qword ptr [rbp - 8], rcx
        mov     dword ptr [rbp - 12], eax
        mov     rdi, qword ptr [rbp - 8]
        call    __cxa_begin_catch@PLT
        mov     edi, 4
        call    __cxa_allocate_exception@PLT
        mov     rdi, rax
        mov     dword ptr [rdi], -1
        mov     rsi, qword ptr [rip + typeinfo for int@GOTPCREL]
        xor     eax, eax
        mov     edx, eax
        call    __cxa_throw@PLT
        jmp     .LBB1_8
        mov     rcx, rax
        mov     eax, edx
        mov     qword ptr [rbp - 8], rcx
        mov     dword ptr [rbp - 12], eax
        call    __cxa_end_catch@PLT
        jmp     .LBB1_5
.LBB1_5:
        jmp     .LBB1_6
.LBB1_6:
        mov     rdi, qword ptr [rbp - 8]
        call    _Unwind_Resume@PLT
        mov     rdi, rax
        call    __clang_call_terminate
.LBB1_8:

__clang_call_terminate:
        push    rbp
        mov     rbp, rsp
        call    __cxa_begin_catch@PLT
        call    std::terminate()@PLT

main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     dword ptr [rbp - 4], 0
        call    foo()
        add     rsp, 16
        pop     rbp
        ret

DW.ref.__gxx_personality_v0:
        .quad   __gxx_personality_v0
```


</details>
What if we just search for the `__gxx_personality_v0` symbol in the `libcxx` library code? Well, we'll [find it](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libcxxabi/src/cxa_personality.cpp#L1029)\. Cool, thanks\.\.\.

It's scary and confusing, but the most important thing is that we can't yet see a direct connection between this function and that `handler`\. Well, let's try to determine it\!

## libunwind

We know one thing for certain: the `handler` is retrieved from a structure of the `[unw_proc_info_t](https://github.com/llvm/llvm-project/blob/9e25a423d07bdcc2686defedcd509fb405d250db/libunwind/include/libunwind.h#L193)` type\. It contains information about the function that created the current stack frame, as well as additional information needed to unwind the stack\. 

### A super\-quick introduction to libunwind

To trace the connection between the personality routine and this structure, we first need to understand where the structure gets populated\. That means diving into the `libunwind` library\. This library enables reading of function stack frames\. We'll take a look at how its context initialization works under the hood, the data structure it uses to reference stack frames during unwinding, and how those structures are populated\.

To move forward, let's go over the basics of working with `libunwind`\. We can use the structure of the `unw_cursor_t` type to tell the library which frame we want to read\. It's somewhat of a handler that enables us to traverse the stack\. We get the frame information directly from the already familiar `unw_proc_info_t` structure\. The cursor itself is retrieved from a context, which is a structure of the `unw_context_t` type\. Once this structure is initialized, `libunwind` is ready to use\.

### The libunwind context

In our case, `libunwind` gets initialized in `[_Unwind_RaiseException](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindLevel1.c#L470)`, which we covered in the previous article\. It serves as the entry point to the stack unwinding driver when an exception is thrown and is called almost immediately after it occurs\. This happens within the abstraction layer defined by the `the _cxa` family of functions\.

The `libunwind` initialization in this function looks like this:

```cpp
  unw_context_t uc;
  unw_cursor_t cursor;
  __unw_getcontext(&uc);
```

There are two new types here: `[unw_context_t](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/include/libunwind.h#L173)`[ and `unw_cursor_t`](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/include/libunwind.h#L173)\. They have similar definitions: both contain data stored in arrays whose lengths are determined at compile time\. It's impossible to say exactly what's stored there, since the data is located in a 64\-bit unsigned integer array\. This means that the types are opaque wrappers around other structures that are implementation details of the library\. We can determine where the data is populated, though\!

As we can see from the code of the `_Unwind_RaiseException` function, the `unw_context_t` struct is populated in the `[__unw_getcontext](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindRegistersSave.S#L123)` function\.

Wow, we reached assembly code pretty quickly this time\! It does fairly straightforward work—saving the register state into the `unw_context_t` structure, whose pointer we conveniently passed into the function beforehand\. This function has platform\-specific implementations that ensure the array within this struct is populated correctly\. 

Okay, a bunch of `movq` statements means that `libunwind` is initialized by saving the function register state where the operation begins\. In our case, this is `[_Unwind_RaiseException](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindLevel1.c#L470)`, the main stack unwinding driver\. This makes sense, since above this function frame are the frames of our program's functions where the exception was thrown\.

Okay, where is the `unw_cursor_t` structure populated?

### The libunwind cursor

The `[Unwind_RaiseException](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindLevel1.c#L470)` consists of two phases, which we covered in the [previous article](https://pvs-studio.com/en/blog/posts/cpp/1333/)\. Let's take a look at the [beginning of the first phase](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libunwind/src/UnwindLevel1.c#L111):

```cpp
static _Unwind_Reason_Code
unwind_phase1(unw_context_t *uc,
              unw_cursor_t *cursor,
              _Unwind_Exception *exception_object)
{
  __unw_init_local(cursor, uc);
  ....
}
```

We can see pointers to the context and the cursor being passed down from the `[_Unwind_RaiseException](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindLevel1.c#L470)` function\. We can also see the `[__unw_init_local](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/libunwind.cpp#L43)` function, which receives those pointers\. From this point on, things are about to get really interesting\. This function determines the register type for the target platform\. In reality, however, the compiler fixes this at compile time\. Then, the function constructs a new `UnwindCursor` object using _placement new_ inside the array stored in the `unw_cursor_t` struct\. 

```cpp
// Use "placement new" to allocate UnwindCursor in the cursor buffer.
new (reinterpret_cast<UnwindCursor<LocalAddressSpace, REGISTER_KIND> *>(cursor))
    UnwindCursor<LocalAddressSpace, REGISTER_KIND>(
        context, LocalAddressSpace::sThisAddressSpace
    );
```

By the way, you might recall that we came across the `UnwindCursor` type in the previous article\. We saw how it handles jumping to the correct point in the program via the [jumpto](https://github.com/llvm/llvm-project/blob/4237ec343a7f0c0d3717972b14ae22ec10ff74cd/libunwind/src/UnwindCursor.hpp#L1428) function\. That's why we'll take a closer look at it\! In any case, [its constructor](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindCursor.hpp#L1379) is easy to find\. The most interesting aspect is how it initializes the `_registers` member variable using a pointer to `unw_context_t`\. The [constructor is located nearby](https://github.com/llvm/llvm-project/blob/4237ec343a7f0c0d3717972b14ae22ec10ff74cd/libunwind/src/Registers.hpp#L359), and the type is defined in an earlier step\.

Note that the cursor moves through different stack frames using the `__unw_step` function\. This topic exceeds the scope of this article, so we'll leave it as homework for the most inquisitive minds\.

### Some info about the procedure in libunwind

Here's the code that kicked off our investigation today:

```cpp
_Unwind_Personality_Fn p = get_handler_function(&frameInfo); 
// ....
_Unwind_Reason_Code personalityResult =(*p)(
    1, action, exception_object->exception_class, 
    exception_object, (struct _Unwind_Context *)(cursor)
);
```

Now that we know the cursor's origin, we can see where the `frameInfo` variable comes from\.

In the code for both phases, we can see that this structure is populated by the `[__unw_get_proc_info](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/libunwind.cpp#L238)` function\. It looks something like this:

```cpp
unw_proc_info_t frameInfo;
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS)
{
  // ....
}
```

Inside `__unw_get_proc_info`, the pointer to `unw_cursor_t` is cast to the pointer to `AbstractUnwindCursor`, and then it calls the `getInfo` member function\. We already saw how an instance of a specific `UnwindCursor` type is created inside the cursor\. `AbstractUnwindCursor` is a virtual interface to objects of this class\.

Inside the `[getInfo](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindCursor.hpp#L3297)` member function, the `unw_proc_info_t` instance stored within the cursor class is copied into the variable we're looking for\. But wait\.\.\. I can't recall seeing anything written to that internal member variable\. Moreover, I remember it being directly zeroed [in the constructor](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindCursor.hpp#L1386)\.

### What did we miss?

The address of our precious `handler` can't be null\. Besides, the address of some handler can't possibly appear in the registers\. Let's face it, up until now, everything we've seen has been focused on preserving register states\. I don't know about you, but I don't remember the System V ABI—or any other, for that matter—describing a separate register for some handler\. So, that information is stored somewhere else\.

Let's look into this some more and return to the `[__unw_init_local](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/libunwind.cpp#L91)` function\. We deliberately omitted the call to the `[setInfoBasedOnIPRegister](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindCursor.hpp#L2722)` function there\. Its name suggests that this might be exactly what we need\. Perhaps the value of the data member in question is set there, among the many conditional compilation directives\.

We have two options here: we can either figure out how binary files are parsed on GNU/Linux to find information for stack tracing, or we can just stop\. Since this doesn't directly relate to the topic of this article, I think it's best to avoid getting bogged down in such details\. Still, let's break down the general points, because that's exactly what makes _table\-based_ exceptions, well, _table\-based_\. 

### Summary N1

Why don't we make an interim summary?

Before moving down the stack, it's necessary to record the current processor state—the values of all its registers\. To do this, we'll use `unw_context_t`\. It's populated using the `__unw_getcontext` function, which is written in the assembly language\. Its task is to simply copy the current register values into this structure\. 

The `unw_cursor_t` structure is used to navigate through stack frames\. A cursor is an opaque pointer\. In fact, it contains a hidden instance of the `UnwindCursor` class\. The cursor is created using the context directly in the memory buffer of the `unw_cursor_t` structure\. 

The `unw_proc_info_t` structure is used to retrieve information about the function that created the stack frame we're interested in\. The `__unw_get_proc_info` function populates the structure\. It takes our cursor and pulls details about the current function from its internal data \(the `_info` data member\)\.

The key question is this: if the cursor constructor simply zeroes everything, where does it get function\-specific data, such as the address of the exception handler \(i\.e\., the personality routine\)?

![1360_LinuxExceptionTwo/image2.png](https://import.viva64.com/docx/blog/1360_LinuxExceptionTwo/image2.png)

## ELF, DWARF, and other xenos species

To get a better grasp of the issue, let's expand a bit the assembly code we obtained from our test example at the very beginning\. Oh no, we won't be adding anything to the original C\+\+ source code\. Instead, we'll take a look at the complete version of this assembly code\. It contains the code fragment directly related to our C\+\+ source code, as well as various additional directives\. I won't include the code in the article itself, as it's way too long\. Let's look at some fragments below; the full code is available [here](https://godbolt.org/z/cnrz7KE6x)\.

### The \.cfi directives

The code has become much longer due to the additional metadata\. We'll focus on the `.cfi*` family of directives that enable the debugger and profilers to traverse the stack, restore the frame state, and perform other tasks\. 

They don't execute code, but instead create _unwind tables_, also known as _exception frames_\. The tables aren't in the assembly file; the compiler creates them in the final binary\. You can view them using, for example, the `readelf -w` command\.

<details>
   <summary>Here's an example of running the 'readelf \\\-w' main command</summary>

```cpp
$ readelf -w main
Contents of the .eh_frame section:

00000000 0000000000000014 00000000 CIE
  Version:               1
  Augmentation:          "zR"
  Code alignment factor: 1
  Data alignment factor: -8
  Return address column: 16
  Augmentation data:     1b
  DW_CFA_def_cfa: r7 (rsp) ofs 8
  DW_CFA_offset: r16 (rip) at cfa-8
  DW_CFA_nop
  DW_CFA_nop

00000018 0000000000000014 0000001c FDE cie=00000000
pc=00000000000010a0..00000000000010c6
  DW_CFA_advance_loc: 4 to 00000000000010a4
  DW_CFA_undefined: r16 (rip)
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

00000030 0000000000000024 00000034 FDE cie=00000000
pc=0000000000001020..0000000000001090
  DW_CFA_def_cfa_offset: 16
  DW_CFA_advance_loc: 6 to 0000000000001026
  DW_CFA_def_cfa_offset: 24
  DW_CFA_advance_loc: 10 to 0000000000001030
  DW_CFA_def_cfa_expression ; ...
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
```




</details>


They allow the stack unwinder to restore stack frame states using only the instruction pointer value\. It looks suspiciously similar to the debugger information in debug builds, because that's exactly what it is\. The debugger does pretty much the same thing\. The table format is defined in the DWARF standard, which is why the exception\-handling approach discussed here is sometimes referred to as _DWARF\-based_\.

These directives are all necessary for a program, such as a debugger, to retrieve information about the currently examined stack frame\. This topic is beyond the scope of this article\. There are three ways to learn more: read a [brief note](https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html) about the format of these tables and leave it at that, check out [this excellent introductory article](https://maskray.me/blog/2020-11-08-stack-unwinding) on the topic, or dig into the [DWARF documentation](https://dwarfstd.org/doc/DWARF5.pdf) \(if you're a true rock and roller\)\.

However, we're interested in a few specific aspects\. Take a look at the `foo` function prologue \(the one that catches the exception in our example\):

```cpp
foo():
.Lfunc_begin1:
        .loc    1 7 0
        .cfi_startproc
        .cfi_personality 155, DW.ref.__gxx_personality_v0
        .cfi_lsda 27, .Lexception0
```

Two directives deserve particular attention: `.cfi_personality` and `.cfi_lsda`\.

Since we're working with LLVM, let's consult their [documentation](https://llvm.org/docs/ExceptionHandling.html)\. There's some interesting content, including a link to [other documentation](https://sourceware.org/binutils/docs/as/CFI-directives.html) that specifically describes the `.cfi` directives we need\. As the description states, the `.cfi_personality` directive specifies where the stack unwinder retrieves the personality routine\. Note that in our code, this instruction contains the ` DW.ref.__gxx_personality_v0` symbol\. The origin of this symbol has remained a mystery to us since the first article\.

Take a look at the code at the very bottom of the assembly file:

```cpp
DW.ref.__gxx_personality_v0:
        .quad   __gxx_personality_v0
        .ident  "clang version 21.1.0
        .section        ".note.GNU-stack","",@progbits
        .addrsig
        .addrsig_sym bar()
        .addrsig_sym __cxa_allocate_exception
        .addrsig_sym __cxa_throw
        .addrsig_sym foo()
        .addrsig_sym __gxx_personality_v0
        .addrsig_sym __cxa_begin_catch
        .addrsig_sym __cxa_end_catch
        .addrsig_sym __clang_call_terminate
        .addrsig_sym _ZSt9terminatev
        .addrsig_sym _Unwind_Resume
        .addrsig_sym _ZTIi
        .section        .debug_line,"",@progbits
```

The address of the `__gxx_personality_v0` function is stored in memory\. Next comes a section containing symbols that we've already encountered\.

The directive in `.cfi_lsda` specifies the location of the _language\-specific data area_\. What's this? Now look at the symbol located near the end of the `foo` function:

```cpp
GCC_except_table1:
.Lexception0:
        .byte   255
        .byte   155
        .uleb128 .Lttbase0-.Lttbaseref0
.Lttbaseref0:
        .byte   1
        .uleb128 .Lcst_end0-.Lcst_begin0
; ...
```

Good news—we've finally found this thing called _exception handling tables_\. More precisely, this is the _language\-specific data area \(lsda\)_, which contains the _exception handling tables_\. There are several of these tables, and we'll take a look at them a little later\.

Okay, we still don't know exactly how the personality routine and lsda work, but at least we've found where their symbols are defined\. In the case of lsda, we also found its exact location\.

### Reading binaries

The way these two components—the personality\-routines and lsda—work is simple: the first looks to the second to determine what to do with the exception in each stack frame it encounters\. We'll see how it works a little later\. For now, let's make sure that the C\+\+ runtime actually reads addresses of the personality routines and lsda\. To do this, we'll go back to the breakdown of the `[setInfoBasedOnIPRegister](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindCursor.hpp#L2722)` function\.

The code contained many conditional compilation directives depending on the runtime's stack unwinding technique\. Since we now know that the method in question is DWARF\-based, we can locate the relevant parts of this function\. There are two of them: [one](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindCursor.hpp#L2814) and [two](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindCursor.hpp#L2837)\.

Let's stop here— describing how to read DWARF structures would make this article endless\. The resources listed above should give you a general idea of the topic\. Long story short, it all comes down to the `[getInfoFromFdeCie](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindCursor.hpp#L1730)` function\. It actually populates the `_info` data member using information obtained from the _unwind tables_, including the addresses of the personality routine and lsda\.

### Summary N2

It seems like now's the time for a little summary\. 

The compiler inserts special directives that begin with `.cfi_` \(Call Frame Information\) when converting C\+\+ code into assembly\. The processor doesn't actually execute these directives—they instruct the linker to build unwinding tables from this data\.

We've already seen a few of them:

* `.cfi_personality` specifies the address of the exception handler \(the personality routine\) for the function;
* `.cfi_lsda` points to the Language Specific Data Area \(LSDA\)—function\-specific metadata that tells the runtime how to handle an exception when it processes a particular stack frame\.

After compilation, all `.cfi` directives are converted into binary structures, which we can view using the `readelf -w` command\.  They contain `.eh_frame` entries with general information on how to unwind the stack for each function\.

How exactly does the personality routine read the lsda, though?

![1360_LinuxExceptionTwo/image3.png](https://import.viva64.com/docx/blog/1360_LinuxExceptionTwo/image3.png)

## The personality routine, lsda, and two types of unwinding

The challenge with the personality routine is that it performs many similar actions in similar scenarios that still need to be distinguished from one another\. Combine this with various ways to unwind the stack and exceptions that might occur in other languages, and the result is a Frankenstein's monster\. Still, all these parts serve the same goal, and that's where we'll start\.

The personality routine parses the lsda to determine how to handle the exception within the context of a specific stack frame\. In other words, it must know how to parse the stack in order to retrieve from memory the data contained in the lsda\. Although this routine involves low\-level work, it's conceptually quite high up in the Itanium ABI, at a level that defines language\-specific functionality\. 

In the previous article, we looked at an interface from the `_cxa*` family responsible for exception allocation, throwing, and catching\. This behavior applies only to the C\+\+ language and isn't related to stack unwinding, which conceptually operates at a lower level\. The personality routine is on the same level as the `_cxa*` family, and it acts as a callback invoked by other subsystems during stack unwinding\.

There are two unwinding contexts where the personality routine is called: an exception throw and something else\. The first one is initiated in the `_Unwind_RaiseException` and `_Unwind_SjLj_RaiseException` functions, while `_Unwind_ForcedUnwind` is responsible for everything else \(such as unwinding the stack when exiting a thread\)\. An exception throw consists of two stages: finding the appropriate `catch` block and cleaning up the stack\. We covered all of this in the previous article\.

The `[_Unwind_Action](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libunwind/include/unwind.h#L54)` defines the specific context\. It shows that during the second phase of exception handling, the routine can do one of two things: either clean up the current frame or transfer control to a catch block identified during the first phase\. It also includes a separate case for reaching the end of the stack during unwinding without an exception\. 

### The personality routine algorithm

Okay, let's take a look at the [code of this routine](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libcxxabi/src/cxa_personality.cpp#L1029)\. We'll skip the details of parsing the lsda for now and try to grasp the general logic\. For the sake of simplicity, we'll remove the conditional compilation that isn't relevant to our stack unwinding approach or platform\.

The simplest case occurs when, during the second phase, we reach the same frame that we marked in the first phase as having a valid `catch` block\.

```cpp
if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME) &&
    native_exception) {
    // Reload the results from the phase 1 cache.
    __cxa_exception* exception_header =
        (__cxa_exception*)(unwind_exception + 1) - 1;
    results.ttypeIndex = exception_header->handlerSwitchValue;
    results.actionRecord = exception_header->actionRecord;
    results.languageSpecificData = exception_header->languageSpecificData;
    set_landing_pad(results, exception_header->catchTemp);
    results.adjustedPtr = exception_header->adjustedPtr;
    set_registers(unwind_exception, context, results);
    if (results.ttypeIndex < 0) {
      exception_header->catchTemp = 0;
    }
    return _URC_INSTALL_CONTEXT;
}
```

At this point, the routine retrieves the information it gathered during the previous phase and returns the `_URC_INSTALL_CONTEXT` value\. This tells `[unwind_phase2](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libunwind/src/UnwindLevel1.c#L212C1-L212C14)` that it's time to move to the `catch` block\. The exact landing point comes from the cursor state [passed when calling the routine](https://github.com/llvm/llvm-project/blob/7c36e1d424a782077ea22363e906367691676025/libunwind/src/UnwindLevel1.c#L304) and set in the `[set_registers](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libcxxabi/src/cxa_personality.cpp#L592)` function\.

In all other cases, that is, if we haven't reached the end of the exception throw's second phase, we need to parse the lsda to determine the next step:

```cpp
scan_eh_tab(results, actions, native_exception, unwind_exception, context);
```

The following code is a bit unintuitive:

```cpp
if (results.reason == _URC_CONTINUE_UNWIND ||
    results.reason == _URC_FATAL_PHASE1_ERROR)
    return results.reason;

if (actions & _UA_SEARCH_PHASE)
{
    assert(results.reason == _URC_HANDLER_FOUND);
    if (native_exception) {
        __cxa_exception* exc = (__cxa_exception*)(unwind_exception + 1) - 1;
        exc->handlerSwitchValue = static_cast<int>(results.ttypeIndex);
        exc->actionRecord = results.actionRecord;
        exc->languageSpecificData = results.languageSpecificData;
        get_landing_pad(exc->catchTemp, results);
        exc->adjustedPtr = results.adjustedPtr;
    }
    return _URC_HANDLER_FOUND;
}
```

`_URC_CONTINUE_UNWIND` can be set in either the first or second phase\. The first one is clear: a handler hasn't been found at that point\. In the second phase, this value is set if the stack in question contains neither `catch` blocks nor automatic variables whose destructors should be called when unwinding the stack\.

`_URC_FATAL_PHASE1_ERROR` is set when the parser finds something in lsda that shouldn't be there\. This indicates an error\.

Otherwise, there's only one thing left to do in the first phase—celebrate finding the right handler\! For native C\+\+ exceptions, we store the relevant information for later \(we saw it being used toward the end of the second phase earlier\)\. For foreign exceptions, we simply report finding the handler\. In both cases, the routine returns `_URC_HANDLER_FOUND`\.

At this point, only one option remains: we're in the second phase and need to perform some action in the current stack frame:

```cpp
assert(actions & _UA_CLEANUP_PHASE);
assert(results.reason == _URC_HANDLER_FOUND);
```

What can it be? Given that we'd enter the valid `catch` block right at the beginning of the function, we have two options: to either call stack variable destructors, or handle [exception specifications](https://en.cppreference.com/w/cpp/language/except_spec.html):

```cpp
set_registers(unwind_exception, context, results);
if (results.ttypeIndex < 0) {
  __cxa_exception* exception_header =
        (__cxa_exception*)(unwind_exception + 1) - 1;
  exception_header->catchTemp = 0;
}
return _URC_INSTALL_CONTEXT;
```

The last line is all we need here\. The `_URC_INSTALL_CONTEXT` value instructs the runtime to transfer control to the code fragment the routine found\. This is a _landing pad_, which is a block of code that either handles an exception or an exception specification, or it calls destructors\.

### The lsda structure

The lsda is parsed in LLVM within the `[scan_eh_tab](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libcxxabi/src/cxa_personality.cpp#L645)` function\. Analyzing how it works would be interesting, but instead, explaining what exactly this lsda contains—with varying degrees of clarity—seems more useful\.

As we previously learned, the lsda includes exception handling tables\. The name kind of hints that there are multiple of those\. The [specification does confirm](https://itanium-cxx-abi.github.io/cxx-abi/exceptions.pdf) this\.

The first one is called the _call sites table_\. It's a list of entries containing pointers to the start and end of an instruction sequence containing the instruction from which the exception was thrown\. With the address of this instruction \(the unwinder restores it from the DWARF tables we discussed above\), we can understand which entry to use\. The call site itself is a code snippet located either inside or outside the `try` block\. It's kind of a critical section where specific actions happen during stack unwinding\. The list of possible actions follows the call sites table, and the required action is identified by an index that is stored alongside pointers to the start and end of a specific call site\.

Right after the call site table comes the _action table_, which is a list of actions associated with a particular call site\. Although it's called a "table," it's actually a linked list\. In addition to a pointer to the next node, each element contains a _type index_, which is a multipurpose data member of an integer type\. If it's zero, then destructors are called in the current stack frame\. Control must be passed to the code that contains calls generated by the compiler\. If it's greater than zero, then we're dealing with the `catch` block, and it's necessary to check whether the `catch` block type matches the exception type\. In this case, the number acts as an index to a table of types supported by the `catch` blocks of this frame \(that table follows the current one\)\. If the index is negative, then we're dealing with an exception specification, and it's necessary to determine whether the function can throw an exception of that type\.

Following the action table is the _type table_, which contains references to the `std::type_info` objects for the types supported by the `catch` blocks of the current frame\. This table is indexed by the positive indices contained in the nodes of the action table linked list\.

Somewhere nearby—depending on the implementation—is a list of references to the `std::type_info` types listed in the exception specification\. If you're using older standards that allow such specifications, this is where the runtime handles unsupported types\. In this case, the handler logic works in reverse: `[__cxa_call_unexpected](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libcxxabi/src/cxa_personality.cpp#L1282)` is called when an exception lands in the generated code after violating the specification\. That function ultimately behaves similarly to the deprecated [`std::unexpected`](https://en.cppreference.com/w/cpp/error/unexpected.html)\.

The structure of this mess looks something like this:

```cpp
========================
Lsda header
LPStart Encoding
LPStart (optional)
TType Encoding
TType Offset (optional)
Call-Site Encoding
Call-Site Table Length
========================
Call-Site Table
------------------------
start of a call site
length of a call site
landingPad (handler) offset (from landingPad base)
actionEntry (1-based offset)
------------------------
------------------------
start of a call site
length of a call site
landingPad (handler) offset (from landingPad base)
actionEntry (1-based offset)
------------------------
========================
Action Table
------------------------
ttypeIndex = 3
nextOffset = -3 
------------------------
------------------------
ttypeIndex = 2
nextOffset = -3 
------------------------
------------------------
ttypeIndex = 1
nextOffset = 0
------------------------
========================
Type Table (RTTI)
------------------------
index 1 ──> typeinfo(float)
------------------------
------------------------
index 2 ──> typeinfo(int)
------------------------
------------------------
index 3 ──> typeinfo(T)
------------------------
========================
```

### Summary N3

That's the way the ball bounces\. Let's summarize\!

The personality routine can parse the lsda\. The lsda contains language\-specific information about what should happen in a given stack frame when an exception is thrown\. The lsda is generated by the compiler and appended to the function body\.

The personality routine is called from the `_Unwind*` family of functions, which are stack unwinding drivers\. These functions form a layer independent of any programming language\. In other words, a routine is a callback that logically belongs to a language\-specific layer\.

We also learned about the "landing pad" concept\. This is essentially a generalization over the `catch` block, which can handle exception specifications and destructor calls in addition to the exceptions\.

So, how does this landing pad actually work?

## The last mile and landing pad

We've slightly modified the code for clarity:

```cpp
#include <stdlib.h>

int bar() throw (int)
{
    return (rand() % 2) ? throw -1 : -666;
}

int foo()
{
    try {
        return bar();
    }
    catch(float) {
        return 69;
    }
    catch(int) {
        return 42;
    }
    catch (...) {
        //skiped intentionally
    }
    return 666;
}

int main()
{
    return foo();
}
```

The assembly code is below\. The [link leads](https://godbolt.org/z/7sfqczxss) to the same file, but it has additional compiler directives\.

<details>
   <summary>Assembly code</summary>

```cpp
bar():
        push    rbp
        mov     rbp, rsp
        sub     rsp, 32
        call    rand@PLT
        mov     ecx, 2
        cdq
        idiv    ecx
        mov     byte ptr [rbp - 9], 0
        cmp     edx, 0
        je      .LBB0_2
        mov     edi, 4
        call    __cxa_allocate_exception@PLT
        mov     rdi, rax
        mov     qword ptr [rbp - 8], rdi
        mov     byte ptr [rbp - 9], 1
        mov     dword ptr [rdi], -1
        mov     rsi, qword ptr [rip + typeinfo for int@GOTPCREL]
        xor     eax, eax
        mov     edx, eax
        call    __cxa_throw@PLT
        jmp     .LBB0_8
.LBB0_2:
        jmp     .LBB0_3
.LBB0_3:
        mov     eax, 4294966630
        add     rsp, 32
        pop     rbp
        ret
        mov     rcx, rax
        mov     eax, edx
        mov     qword ptr [rbp - 24], rcx
        mov     dword ptr [rbp - 28], eax
        cmp     dword ptr [rbp - 28], 0
        jge     .LBB0_7
        mov     rdi, qword ptr [rbp - 24]
        call    __cxa_call_unexpected@PLT
.LBB0_7:
        mov     rdi, qword ptr [rbp - 24]
        call    _Unwind_Resume@PLT
.LBB0_8:

foo():
        push    rbp
        mov     rbp, rsp
        sub     rsp, 48
        call    bar()
        mov     dword ptr [rbp - 32], eax
        jmp     .LBB1_1
.LBB1_1:
        mov     eax, dword ptr [rbp - 32]
        mov     dword ptr [rbp - 4], eax
        jmp     .LBB1_9
        mov     rcx, rax
        mov     eax, edx
        mov     qword ptr [rbp - 16], rcx
        mov     dword ptr [rbp - 20], eax
        mov     eax, dword ptr [rbp - 20]
        mov     dword ptr [rbp - 36], eax
        mov     ecx, 3
        cmp     eax, ecx
        jne     .LBB1_5
        mov     rdi, qword ptr [rbp - 16]
        call    __cxa_begin_catch@PLT
        movss   xmm0, dword ptr [rax]
        movss   dword ptr [rbp - 28], xmm0
        mov     dword ptr [rbp - 4], 69
        call    __cxa_end_catch@PLT
        jmp     .LBB1_9
.LBB1_5:
        mov     eax, dword ptr [rbp - 36]
        mov     ecx, 2
        cmp     eax, ecx
        jne     .LBB1_7
        mov     rdi, qword ptr [rbp - 16]
        call    __cxa_begin_catch@PLT
        mov     eax, dword ptr [rax]
        mov     dword ptr [rbp - 24], eax
        mov     dword ptr [rbp - 4], 42
        call    __cxa_end_catch@PLT
        jmp     .LBB1_9
.LBB1_7:
        mov     rdi, qword ptr [rbp - 16]
        call    __cxa_begin_catch@PLT
        call    __cxa_end_catch@PLT
        mov     dword ptr [rbp - 4], 666
.LBB1_9:
        mov     eax, dword ptr [rbp - 4]
        add     rsp, 48
        pop     rbp
        ret

main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     dword ptr [rbp - 4], 0
        call    foo()
        add     rsp, 16
        pop     rbp
        ret

DW.ref.__gxx_personality_v0:
        .quad   __gxx_personality_v0
```


</details>
First, let's look at this little example with the `bar` function:

```cpp
.LBB0_3:
        mov     eax, 4294966630
        add     rsp, 32
        pop     rbp
        ret
        mov     rcx, rax
        mov     eax, edx
        mov     qword ptr [rbp - 24], rcx
        mov     dword ptr [rbp - 28], eax
        cmp     dword ptr [rbp - 28], 0
        jge     .LBB0_7
        mov     rdi, qword ptr [rbp - 24]
        call    __cxa_call_unexpected@PLT
```

It's interesting because it shows how exception specifications are handled\. If the function throws an exception of a type not listed in its specification, the personality routine will read the lsda and instruct the unwinder to transfer control to this point\. Calling `__cxa_call_unexpected` is usually something that your program won't survive\. 

Another interesting detail is in the `foo` function\. It handles the selection of the specific `catch` block that processes the exception\. The lsda provides the runtime with a pointer to a landing pad\. However, that landing pad represents a _handler in general_ rather than a specific one\. With `catch` blocks, it points to the start of the entire `catch` block section that follows a given `try` block, rather than to a specific handler\.

Let's take, for example, an exception handler for the `int` type:

```cpp
.LBB1_5:
        mov     eax, dword ptr [rbp - 36]
        mov     ecx, 2
        cmp     eax, ecx
        jne     .LBB1_7
        mov     rdi, qword ptr [rbp - 16]
        call    __cxa_begin_catch@PLT
        mov     eax, dword ptr [rax]
        mov     dword ptr [rbp - 24], eax
        mov     dword ptr [rbp - 4], 42
        call    __cxa_end_catch@PLT
        jmp     .LBB1_9
; ...
.LBB1_9:
        mov     eax, dword ptr [rbp - 4]
        add     rsp, 48
        pop     rbp
        ret
```

In addition to the usual things like calling `__cxa_begin_catch` and `__cxa_end_catch`, we can see some code that looks strange at first glance:

```cpp
        mov     eax, dword ptr [rbp - 36]
        mov     ecx, 2
        cmp     eax, ecx
        jne     .LBB1_7
```

In this fragment, a conditional jump occurs if something isn't equal to `2`\. `2` of what? Why not `3`?

Think back to how we discussed tables within the lsda earlier\. It contained a type table indexed by numbers found in entries of the action table\. In this assembly code, `2` is an index that the runtime [kindly stored](https://github.com/llvm/llvm-project/blob/bd0769ef869a1341e8122978e1eafc78c5f3d312/libcxxabi/src/cxa_personality.cpp#L1080) in the `__cxa_exception` structure of the thrown exception when calling the personality routine\. If the program had thrown a different exception type that still matched one of the available `catch` blocks, the runtime would've stored a different index to identify the correct type\.

That's it—it wasn't so bad after all\! There isn't much to summarize here since this part doesn't introduce many new concepts\.

![1360_LinuxExceptionTwo/image4.png](https://import.viva64.com/docx/blog/1360_LinuxExceptionTwo/image4.png)

## Is this the end?

Dear friends, if you're reading this, I want you to know that you're real heroes, and I admire you\. Getting through all of this wasn't easy\. I hope it was worth the effort, and you learned something cool—or at least something new—along the way\!

One question remains unanswered: how do exceptions over the `setjmp`/ `longjmp` mechanism work? That's a great reason to revisit the topic in the future\!

I'd love to read your thoughts in the comments—hope to see you there\!

_El Psy Kongroo_\.