diff --git a/riscv-elf.md b/riscv-elf.md
index d47e4e43..5c34ad23 100644
--- a/riscv-elf.md
+++ b/riscv-elf.md
@@ -21,7 +21,10 @@
* [Note Sections](#note-sections)
* [Dynamic Table](#dynamic-table)
* [Hash Table](#hash-table)
-4. [DWARF](#dwarf)
+4. [Exception Handling](#exception-handling)
+ * [Encodings](#exception-handling-encodings)
+ * [Registers](#exception-handling-registers)
+5. [DWARF](#dwarf)
* [Dwarf Register Numbers](#dwarf-register-numbers)
## Copyright and license information
@@ -436,7 +439,8 @@ Enum | ELF Reloc Type | Description | Details
54 | R_RISCV_SET8 | Local label subtraction |
55 | R_RISCV_SET16 | Local label subtraction |
56 | R_RISCV_SET32 | Local label subtraction |
-57-191 | *Reserved* | Reserved for future standard use |
+57 | R_RISCV_32_PCREL | PC-relative 32-bit offset | Primarily used for FDE start location in .eh_frame
+58-191 | *Reserved* | Reserved for future standard use |
192-255 | *Reserved* | Reserved for nonstandard ABI extensions |
Nonstandard extensions are free to use relocation numbers 192-255 for any
@@ -791,6 +795,79 @@ typedef struct
## Hash Table
+# Exception Handling
+
+Exception handling is achieved using C++ exceptions based on DWARF-derived
+CIE (Common Information Entry) and FDE (Frame Descriptor Entry) data
+structures saved in `.eh_frame`, plus additional data
+structures in the `.gcc_except_table` section of the ELF. Some relevant
+fields in these data structures are defined below:
+
+## Encodings
+
+### Frame Descriptor Entry (FDE) encoding
+
+Used to encode the range of code addresses which the current Frame
+Descriptor Entry applies to. Generally encoded as:
+
+```
+DW_EH_PE_pcrel | DW_EH_PE_sdata4
+```
+
+This encoding is used both to specify the start address for the code
+that the FDE is applicable to, and the length of the code which
+the FDE applies to. In the first instance, a 32-bit signed offset
+is emitted from the current position in the FDE to the code address
+itself, and this generates a `R_RISCV_32_PCREL` relocation. In the
+second instance, a `R_RISCV_ADD32` and `R_RISCV_SUB32` pair is
+emitted which encodes the difference between the end address and
+start address in the code.
+
+### Language Specific Data Area (LSDA) encoding
+
+Encodes refences to the LSDA for the current FDE. Like the FDE encoding
+this is generally encoded as:
+
+```
+DW_EH_PE_pcrel | DW_EH_PE_sdata4
+```
+
+This causes a `R_RISCV_ADD32` and `R_RISCV_SUB32` relocation pair
+to be emitted, encoding the offset from the position the LSDA is
+encoded in the FDE (in the `.eh_frame` section) to the LSDA data
+structure itself (which is found in the `.gcc_except_table` section).
+
+### Personality Function encoding
+
+Use to encode references to the personality function for the current
+CIE. Unlike the LSDA and FDE, the personality function is encoded
+with an additional level of indirection.
+
+```
+DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4
+```
+
+This is also encoded as a `R_RISCV_ADD32` and `R_RISCV_SUB32` pair,
+however the target is not the direct address of the
+`__gxx_personality_v0` function, but instead references a temporary
+symbol `DW.ref.__gxx_personality_v0` which contains the full address
+of `__gxx_personality_v0` and is found in a special section
+`.sdata.DW.ref.__gxx_personality_v0`.
+
+The reason for this is that it allows the `.eh_frame` section to
+contain no dynamic relocations - since `__gxx_personality_v0` may
+be in a shared object - and mean the `.eh_frame` section does not
+need to be writeable at runtime. Instead, only the new temporary
+`.sdata.DW.ref.__gxx_personality_v0` section is writeable.
+
+## Registers
+
+When an exception is caught, data needs to be passed from the
+personality function to the landing pad. Four scratch registers are
+used for this purpose, and these parameters are passed to the landing
+pad according to the integer calling convention; they are passed in
+registers `a0`, `a1`, `a2` and `a3`.
+
# DWARF
Dwarf Register Numbers