1
1
# Exception handling
2
2
3
- During the "Memory layout" section we decided to start out simple and leave out handling of
4
- exceptions. In this section we'll add support for handling exceptions ; this serves as an example of
3
+ During the "Memory layout" section, we decided to start out simple and leave out handling of
4
+ exceptions. In this section, we'll add support for handling them ; this serves as an example of
5
5
how to achieve compile time overridable behavior in stable Rust (i.e. without relying on the
6
6
unstable ` #[linkage = "weak"] ` attribute, which makes a symbol weak).
7
7
8
8
## Background information
9
9
10
10
In a nutshell, * exceptions* are a mechanism the Cortex-M and other architectures provide to let
11
- applications respond to asynchronous, usually external, events. The exception mechanism works like
12
- this: when the processor receives a signal or event associated to a type of exception it suspends
11
+ applications respond to asynchronous, usually external, events. The most prominent type of exception,
12
+ that most people will know, is the classical (hardware) interrupt.
13
+
14
+ The Cortex-M exception mechanism works like this:
15
+ When the processor receives a signal or event associated to a type of exception, it suspends
13
16
the execution of the current subroutine (by stashing the state in the call stack) and then proceeds
14
17
to execute the corresponding exception handler, another subroutine, in a new stack frame. After
15
- finishing the execution of the exception handler (i.e. returning from it) the processor resumes the
18
+ finishing the execution of the exception handler (i.e. returning from it), the processor resumes the
16
19
execution of the suspended subroutine.
17
20
18
21
The processor uses the vector table to decide what handler to execute. Each entry in the table
19
22
contains a pointer to a handler, and each entry corresponds to a different exception type. For
20
23
example, the second entry is the reset handler, the third entry is the NMI (Non Maskable Interrupt)
21
24
handler, and so on.
22
25
23
- As mentioned before the processor expects the vector table to be at some specific location and each
24
- entry in it can potentially be used by the processor at runtime so they must always contain valid
25
- values. Furthermore, we want the ` rt ` crate to be flexible so the end user can customize the
26
- behavior of each exception handler. Finally, the vector table is read only memory, or rather in not
26
+ As mentioned before, the processor expects the vector table to be at some specific location in memory,
27
+ and each entry in it can potentially be used by the processor at runtime. Hence, the entries must always
28
+ contain valid values. Furthermore, we want the ` rt ` crate to be flexible so the end user can customize the
29
+ behavior of each exception handler. Finally, the vector table resides in read only memory, or rather in not
27
30
easily modified memory, so the user has to register the handler statically, rather than at runtime.
28
31
29
- To satisfy all these constraints we'll assign a * default* value to all the entries of the vector
30
- table in the ` rt ` crate but make these values kind of * weak* to let the end user override them
32
+ To satisfy all these constraints, we'll assign a * default* value to all the entries of the vector
33
+ table in the ` rt ` crate, but make these values kind of * weak* to let the end user override them
31
34
at compile time.
32
35
33
36
## Rust side
@@ -36,8 +39,8 @@ Let's see how all this can be implemented. For simplicity, we'll only work with
36
39
of the vector table; these entries are not device specific so they have the same function on any
37
40
kind of Cortex-M microcontroller.
38
41
39
- The first thing we'll do is create an array of vectors (pointers to exception handlers) in the Rust
40
- code:
42
+ The first thing we'll do is create an array of vectors (pointers to exception handlers) in the
43
+ ` rt ` crate's code:
41
44
42
45
``` rust
43
46
pub union Vector {
@@ -83,7 +86,7 @@ should be assigned the value `0` so we use a union to do exactly that. The entri
83
86
to a handler make use of * external* functions; this is important because it lets the end user
84
87
* provide* the actual function definition.
85
88
86
- Next we define a default exception handler in the Rust code. Exceptions that have not been assigned
89
+ Next, we define a default exception handler in the Rust code. Exceptions that have not been assigned
87
90
a handler by the end user will make use of this default handler.
88
91
89
92
``` rust
@@ -130,8 +133,9 @@ PROVIDE(PendSV = DefaultExceptionHandler);
130
133
PROVIDE(SysTick = DefaultExceptionHandler);
131
134
```
132
135
133
- ` PROVIDE ` only takes effect when symbol on the RHS is still undefined after inspecting all the input
134
- object files. This is the scenario where the user didn't assign a handler to the exception.
136
+ ` PROVIDE ` only takes effect when the symbol to the left of the equal sign is still undefined after
137
+ inspecting all the input object files. This is the scenario where the user didn't implement the
138
+ handler for the respective exception.
135
139
136
140
## Testing it
137
141
@@ -214,9 +218,24 @@ Contents of section .vector_table:
214
218
0030 00000000 00000000 79000000 79000000 ........y...y...
215
219
```
216
220
221
+ The vector table now resembles the results of all the code snippets in this book so far. To summarize:
222
+ - In the [ _ Inspecting it_ ] section of the earlier memory chapter, we learned that:
223
+ - The first entry in the vector table contains the initial value of the stack pointer.
224
+ - Objdump prints in ` little endian ` format, so the stack starts at ` 0x2001_0000 ` .
225
+ - The second entry points to address ` 0x0000_0041 ` , the Reset handler.
226
+ - The address of the Reset handler can be seen in the disassembly above, being ` 0x40 ` .
227
+ - The first bit being set to 1 does not alter the address due to alignment requirements. Instead, it causes the function to be executed in _ thumb mode_ .
228
+ - Afterwards, a pattern of addresses alternating between ` 0x79 ` and ` 0x00 ` is visible.
229
+ - Looking at the disassembly above, it is clear that ` 0x79 ` refers to the ` DefaultExceptionHandler ` (` 0x78 ` executed in thumb mode).
230
+ - Cross referencing the pattern to the vector table that was set up earlier in this chapter (see the definition of ` pub static EXCEPTIONS ` ) with [ the vector table layout for the Cortex-M] , it is clear that the address of the ` DefaultExceptionHandler ` is present each time a respective handler entry is present in the table.
231
+ - In turn, it is also visibile that the layout of the vector table data structure in the Rust code is aligned with all the reserved slots in the Cortex-M vector table. Hence, all reserved slots are correctly set to a value of zero.
232
+
233
+ [ _Inspecting it_ ] : https://rust-embedded.github.io/embedonomicon/memory-layout.html#inspecting-it
234
+ [ the vector table layout for the Cortex-M ] : https://developer.arm.com/docs/dui0552/latest/the-cortex-m3-processor/exception-model/vector-table
235
+
217
236
## Overriding a handler
218
237
219
- To override an exception handler the user has to provide a function whose symbol name exactly
238
+ To override an exception handler, the user has to provide a function whose symbol name exactly
220
239
matches the name we used in ` EXCEPTIONS ` .
221
240
222
241
``` rust
@@ -262,9 +281,9 @@ Process 1 stopped
262
281
The program now executes the user defined ` HardFault ` function instead of the
263
282
` DefaultExceptionHandler ` in the ` rt ` crate.
264
283
265
- Like our first attempt at a ` main ` interface this first implementation has the problem of having no
284
+ Like our first attempt at a ` main ` interface, this first implementation has the problem of having no
266
285
type safety. It's also easy to mistype the name of the exception, but that doesn't produce an error
267
- or warning instead the user defined handler is simply ignored. Those problems can be fixed using a
286
+ or warning. Instead the user defined handler is simply ignored. Those problems can be fixed using a
268
287
macro like the [ ` exception! ` ] macro defined in ` cortex-m-rt ` .
269
288
270
289
[ `exception!` ] : https://github.com/japaric/cortex-m-rt/blob/v0.5.1/src/lib.rs#L79
0 commit comments