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