Skip to content

Commit 759580f

Browse files
Merge #11
11: Review of memory-layout r=japaric a=andre-richter Co-authored-by: Andre Richter <[email protected]>
2 parents c8bf279 + 330af10 commit 759580f

File tree

1 file changed

+36
-33
lines changed

1 file changed

+36
-33
lines changed

src/memory-layout.md

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ the rest of pointers are related to exceptions -- we'll ignore them for now.
1717
[vector table]: https://developer.arm.com/docs/dui0552/latest/the-cortex-m3-processor/exception-model/vector-table
1818

1919
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
2121
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).
2423

2524
[linker scripts]: https://sourceware.org/binutils/docs/ld/Scripts.html
2625

@@ -40,19 +39,20 @@ section placement via these attributes:
4039
`#[no_mangle] fn bar()` will produce a symbol named `bar`.
4140
- `#[link_section = ".bar"]` places the symbol in a section named `.bar`.
4241

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.
4443

4544
## The Rust side
4645

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.
5050

5151
The reset vector is a pointer into the reset handler. The reset handler is the function that the
5252
device will execute after a system reset, or after it powers up for the first time. The reset
5353
handler is always the first stack frame in the hardware call stack; returning from it is undefined
5454
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(/* .. */) -> !`.
5656

5757
``` rust
5858
// The reset handler
@@ -70,16 +70,16 @@ pub unsafe extern "C" fn Reset() -> ! {
7070
pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
7171
```
7272

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.
7575

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.
8080

8181
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
8383
make a symbol external in Rust is to make its corresponding item public (`pub`) and *reachable* (no
8484
private module between the item and the root of the crate).
8585

@@ -137,35 +137,35 @@ in the target. The values used here correspond to the LM3S6965 microcontroller.
137137

138138
Here we indicate to the linker that the reset handler -- whose symbol name is `Reset` -- is the
139139
*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.
142142

143143
### `EXTERN`
144144

145145
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`.
150150

151151
### `SECTIONS`
152152

153153
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:
156156

157157
```
158158
.vector_table ORIGIN(FLASH) : { /* .. */ } > FLASH
159159
```
160160

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,
162162

163163
```
164164
.text : { /* .. */ } > FLASH
165165
```
166166

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,
169169
`.vector_table`.
170170

171171
The output `.vector_table` section contains:
@@ -187,25 +187,25 @@ memory block.
187187

188188
Next, we use `KEEP` to force the linker to insert all input sections named
189189
`.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.
191191

192192
The output `.text` section contains:
193193

194194
```
195195
*(.text .text.*);
196196
```
197197

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.
200200

201-
Finally, we use the special `/DISCARD/` section to discard:
201+
Finally, we use the special `/DISCARD/` section to discard
202202

203203
```
204204
*(.ARM.exidx.*);
205205
```
206206

207207
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.
209209

210210
## Putting it all together
211211

@@ -221,10 +221,13 @@ use core::panic::PanicInfo;
221221
// The reset handler
222222
#[no_mangle]
223223
pub unsafe extern "C" fn Reset() -> ! {
224+
let x = 42;
225+
226+
// can't return so we go into an infinite loop here
224227
loop {}
225228
}
226229

227-
// The reset vector.
230+
// The reset vector, a pointer into the reset handler
228231
#[link_section = ".vector_table.reset_vector"]
229232
#[no_mangle]
230233
pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
@@ -236,7 +239,7 @@ fn panic(_panic: &PanicInfo) -> ! {
236239
}
237240
```
238241

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
240243
the `arm-none-eabi-gcc` linker that the `thumbv7m-none-eabi` target uses by default. Changing the
241244
linker is done via rustc flags; the full Cargo invocation to change the linker and pass the linker
242245
script to the linker is shown below:

0 commit comments

Comments
 (0)