Skip to content

Commit b53aa95

Browse files
Review of memory-layout
1 parent 48ebec7 commit b53aa95

File tree

1 file changed

+36
-32
lines changed

1 file changed

+36
-32
lines changed

src/memory-layout.md

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +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 the 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
22+
A symbol can be a statically allocated variable, a `static` variable, a set of instructions, or a
2323
monomorphized (non generic) Rust function.
2424

2525
[linker scripts]: https://sourceware.org/binutils/docs/ld/Scripts.html
@@ -40,19 +40,20 @@ section placement via these attributes:
4040
`#[no_mangle] fn bar()` will produce a symbol named `bar`.
4141
- `#[link_section = ".bar"]` places the symbol in a section named `.bar`.
4242

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

4545
## The Rust side
4646

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

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

5758
``` rust
5859
// The reset handler
@@ -70,16 +71,16 @@ pub unsafe extern "C" fn Reset() -> ! {
7071
pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
7172
```
7273

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

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

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

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

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

143144
### `EXTERN`
144145

145146
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`.
150151

151152
### `SECTIONS`
152153

153154
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:
156157

157158
```
158159
.vector_table ORIGIN(FLASH) : { /* .. */ } > FLASH
159160
```
160161

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

163164
```
164165
.text : { /* .. */ } > FLASH
165166
```
166167

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,
169170
`.vector_table`.
170171

171172
The output `.vector_table` section contains:
@@ -187,25 +188,25 @@ memory block.
187188

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

192193
The output `.text` section contains:
193194

194195
```
195196
*(.text .text.*);
196197
```
197198

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

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

203204
```
204205
*(.ARM.exidx.*);
205206
```
206207

207208
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.
209210

210211
## Putting it all together
211212

@@ -221,10 +222,13 @@ use core::panic::PanicInfo;
221222
// The reset handler
222223
#[no_mangle]
223224
pub unsafe extern "C" fn Reset() -> ! {
225+
let x = 42;
226+
227+
// can't return so we go into an infinite loop here
224228
loop {}
225229
}
226230

227-
// The reset vector.
231+
// The reset vector, a pointer into the reset handler
228232
#[link_section = ".vector_table.reset_vector"]
229233
#[no_mangle]
230234
pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
@@ -236,7 +240,7 @@ fn panic(_panic: &PanicInfo) -> ! {
236240
}
237241
```
238242

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

0 commit comments

Comments
 (0)