@@ -17,9 +17,9 @@ the rest of pointers are related to exceptions -- we'll ignore them for now.
17
17
[ vector table ] : https://developer.arm.com/docs/dui0552/latest/the-cortex-m3-processor/exception-model/vector-table
18
18
19
19
Linkers decide the final memory layout of programs, but we can use [ linker scripts] to have some
20
- control over the memory layout . The control granularity that linker scripts give us over the layout
20
+ control over the it . The control granularity that linker scripts give us over the layout
21
21
is at the level of * sections* . A section is a collection of * symbols* laid out in contiguous memory.
22
- A symbol can be a statically allocated variable, a ` static ` variable, or a set of instructions, a
22
+ A symbol can be a statically allocated variable, a ` static ` variable, a set of instructions, or a
23
23
monomorphized (non generic) Rust function.
24
24
25
25
[ linker scripts ] : https://sourceware.org/binutils/docs/ld/Scripts.html
@@ -40,19 +40,20 @@ section placement via these attributes:
40
40
` #[no_mangle] fn bar() ` will produce a symbol named ` bar ` .
41
41
- ` #[link_section = ".bar"] ` places the symbol in a section named ` .bar ` .
42
42
43
- With these attributes we can expose a stable ABI from the program and use it in the linker script.
43
+ With these attributes, we can expose a stable ABI of the program and use it in the linker script.
44
44
45
45
## The Rust side
46
46
47
- We need to populate the first two entries of the vector table. The first one, the initial value for
48
- the stack pointer, can be populated using only the linker script. The second one, the reset vector,
49
- needs to be created in Rust code and placed in the right place using the linker script.
47
+ Like mentioned before, for Cortex-M devices, we need to populate the first two entries of the
48
+ vector table. The first one, the initial value for the stack pointer, can be populated using
49
+ only the linker script. The second one, the reset vector, needs to be created in Rust code
50
+ and placed correctly using the linker script.
50
51
51
52
The reset vector is a pointer into the reset handler. The reset handler is the function that the
52
53
device will execute after a system reset, or after it powers up for the first time. The reset
53
54
handler is always the first stack frame in the hardware call stack; returning from it is undefined
54
55
behavior as there's no other stack frame to return to. We can enforce that the reset handler never
55
- returns by making it a divergent function, a function with signature ` fn(/* .. */) -> ! ` .
56
+ returns by making it a divergent function, which is a function with signature ` fn(/* .. */) -> ! ` .
56
57
57
58
``` rust
58
59
// The reset handler
@@ -70,16 +71,16 @@ pub unsafe extern "C" fn Reset() -> ! {
70
71
pub static RESET_VECTOR : unsafe extern " C" fn () -> ! = Reset ;
71
72
```
72
73
73
- We use ` extern "C" ` to tell the compiler to lower the function using the C ABI instead of the Rust
74
- ABI, which is unstable, as that's what the hardware expects .
74
+ The hardware expects a certain format here, to which we adhere by using ` extern "C" ` to tell the
75
+ compiler to lower the function using the C ABI, instead of the Rust ABI, which is unstable .
75
76
76
- To refer to the reset handler and reset vector from the linker script we need them to have a stable
77
- symbol name so we use ` #[no_mangle] ` . We need fine control over the location of ` RESET_VECTOR ` so we
78
- place it on a known section, ` .vector_table.reset_vector ` . The exact location of the reset handler
79
- itself, ` Reset ` , is not important so we just stick to the default compiler generated section.
77
+ To refer to the reset handler and reset vector from the linker script, we need them to have a stable
78
+ symbol name so we use ` #[no_mangle] ` . We need fine control over the location of ` RESET_VECTOR ` , so we
79
+ place it in a known section, ` .vector_table.reset_vector ` . The exact location of the reset handler
80
+ itself, ` Reset ` , is not important. We just stick to the default compiler generated section.
80
81
81
82
Also, the linker will ignore symbols with internal linkage, AKA internal symbols, while traversing
82
- the list of input object files so we need our two symbols to have external linkage. The only way to
83
+ the list of input object files, so we need our two symbols to have external linkage. The only way to
83
84
make a symbol external in Rust is to make its corresponding item public (` pub ` ) and * reachable* (no
84
85
private module between the item and the root of the crate).
85
86
@@ -137,35 +138,35 @@ in the target. The values used here correspond to the LM3S6965 microcontroller.
137
138
138
139
Here we indicate to the linker that the reset handler -- whose symbol name is ` Reset ` -- is the
139
140
* entry point* of the program. Linkers aggressively discard unused sections. Linkers consider the
140
- entry point and functions called from it as * used* so they won't discard them. Without this line the
141
- linker would discard the ` Reset ` function and all other functions called from it.
141
+ entry point and functions called from it as * used* so they won't discard them. Without this line,
142
+ the linker would discard the ` Reset ` function and all subsequent functions called from it.
142
143
143
144
### ` EXTERN `
144
145
145
146
Linkers are lazy; they will stop looking into the input object files once they have found all the
146
- symbols recursively referenced from the entry point. ` EXTERN ` forces the linker to look for its
147
- argument even after all other referenced symbol have been found. As a rule of thumb, if you need a
148
- symbol that's not called from the entry point to always be present in the output binary you should
149
- use ` EXTERN ` in conjunction with ` KEEP ` .
147
+ symbols that are recursively referenced from the entry point. ` EXTERN ` forces the linker to look
148
+ for ` EXTERN ` 's argument even after all other referenced symbols have been found. As a rule of thumb,
149
+ if you need a symbol that's not called from the entry point to always be present in the output binary,
150
+ you should use ` EXTERN ` in conjunction with ` KEEP ` .
150
151
151
152
### ` SECTIONS `
152
153
153
154
This part describes how sections in the input object files, AKA * input sections* , are to be arranged
154
- in the sections the output object file, AKA output sections; or if they should be discarded. Here we
155
- define two output sections:
155
+ in the sections of the output object file, AKA output sections; or if they should be discarded. Here
156
+ we define two output sections:
156
157
157
158
```
158
159
.vector_table ORIGIN(FLASH) : { /* .. */ } > FLASH
159
160
```
160
161
161
- ` .vector_table ` , which contains the vector table and its located at the start of ` FLASH ` memory;
162
+ ` .vector_table ` , which contains the vector table and is located at the start of ` FLASH ` memory,
162
163
163
164
```
164
165
.text : { /* .. */ } > FLASH
165
166
```
166
167
167
- and ` .text ` , which contains the program subroutines and its located somewhere in ` FLASH ` . Its start
168
- address is not specified but the linker will place after the previous output section,
168
+ and ` .text ` , which contains the program subroutines and is located somewhere in ` FLASH ` . Its start
169
+ address is not specified, but the linker will place it after the previous output section,
169
170
` .vector_table ` .
170
171
171
172
The output ` .vector_table ` section contains:
@@ -187,25 +188,25 @@ memory block.
187
188
188
189
Next, we use ` KEEP ` to force the linker to insert all input sections named
189
190
` .vector_table.reset_vector ` right after the initial SP value. The only symbol located in that
190
- section is ` RESET_VECTOR ` so this will effectively place ` RESET_VECTOR ` second in the vector table.
191
+ section is ` RESET_VECTOR ` , so this will effectively place ` RESET_VECTOR ` second in the vector table.
191
192
192
193
The output ` .text ` section contains:
193
194
194
195
```
195
196
*(.text .text.*);
196
197
```
197
198
198
- All the input sections named ` .text ` and ` .text.* ` . Note that we don't use ` KEEP ` here to let the
199
- linker discard the unused sections.
199
+ This includes all the input sections named ` .text ` and ` .text.* ` . Note that we don't use ` KEEP `
200
+ here to let the linker discard unused sections.
200
201
201
- Finally, we use the special ` /DISCARD/ ` section to discard:
202
+ Finally, we use the special ` /DISCARD/ ` section to discard
202
203
203
204
```
204
205
*(.ARM.exidx.*);
205
206
```
206
207
207
208
input sections named ` .ARM.exidx.* ` . These sections are related to exception handling but we are not
208
- doing stack unwinding on panics and they take up space in Flash memory so we just discard them.
209
+ doing stack unwinding on panics and they take up space in Flash memory, so we just discard them.
209
210
210
211
## Putting it all together
211
212
@@ -221,10 +222,13 @@ use core::panic::PanicInfo;
221
222
// The reset handler
222
223
#[no_mangle]
223
224
pub unsafe extern " C" fn Reset () -> ! {
225
+ let x = 42 ;
226
+
227
+ // can't return so we go into an infinite loop here
224
228
loop {}
225
229
}
226
230
227
- // The reset vector.
231
+ // The reset vector, a pointer into the reset handler
228
232
#[link_section = " .vector_table.reset_vector" ]
229
233
#[no_mangle]
230
234
pub static RESET_VECTOR : unsafe extern " C" fn () -> ! = Reset ;
@@ -236,7 +240,7 @@ fn panic(_panic: &PanicInfo) -> ! {
236
240
}
237
241
```
238
242
239
- We'll use the LLVM linker, LLD, shipped with the Rust toolchain. That way you won't need to install
243
+ We'll use the LLVM linker, LLD, shipped with the Rust toolchain. That way, you won't need to install
240
244
the ` arm-none-eabi-gcc ` linker that the ` thumbv7m-none-eabi ` target uses by default. Changing the
241
245
linker is done via rustc flags; the full Cargo invocation to change the linker and pass the linker
242
246
script to the linker is shown below:
0 commit comments