|
31 | 31 | //!
|
32 | 32 | //! - A `_sheap` symbol at whose address you can locate a heap.
|
33 | 33 | //!
|
| 34 | +//! - Zero cost stack overflow protection when using the `cortex-m-rt-ld` linker. |
| 35 | +//! |
34 | 36 | //! # Example
|
35 | 37 | //!
|
36 | 38 | //! Creating a new bare metal project. (I recommend you use the
|
|
41 | 43 | //! $ cargo new --bin app && cd $_
|
42 | 44 | //!
|
43 | 45 | //! $ # add this crate as a dependency
|
44 |
| -//! $ edit Cargo.toml && cat $_ |
| 46 | +//! $ $EDITOR Cargo.toml && tail $_ |
45 | 47 | //! [dependencies.cortex-m-rt]
|
46 | 48 | //! features = ["abort-on-panic"]
|
47 | 49 | //! version = "0.3.0"
|
48 | 50 | //!
|
49 | 51 | //! $ # tell Xargo which standard crates to build
|
50 |
| -//! $ edit Xargo.toml && cat $_ |
| 52 | +//! $ $EDITOR Xargo.toml && cat $_ |
51 | 53 | //! [dependencies.core]
|
52 | 54 | //! stage = 0
|
53 | 55 | //!
|
|
56 | 58 | //! stage = 1
|
57 | 59 | //!
|
58 | 60 | //! $ # memory layout of the device
|
59 |
| -//! $ edit memory.x && cat $_ |
| 61 | +//! $ $EDITOR memory.x && cat $_ |
60 | 62 | //! MEMORY
|
61 | 63 | //! {
|
62 | 64 | //! /* NOTE K = KiBi = 1024 bytes */
|
63 | 65 | //! FLASH : ORIGIN = 0x08000000, LENGTH = 128K
|
64 | 66 | //! RAM : ORIGIN = 0x20000000, LENGTH = 8K
|
65 | 67 | //! }
|
66 | 68 | //!
|
67 |
| -//! $ edit src/main.rs && cat $_ |
| 69 | +//! $ $EDITOR src/main.rs && cat $_ |
68 | 70 | //! ```
|
69 | 71 | //!
|
70 | 72 | //! ``` ignore,no_run
|
|
102 | 104 | //! 8000400: b580 push {r7, lr}
|
103 | 105 | //! 8000402: 466f mov r7, sp
|
104 | 106 | //! 8000404: b084 sub sp, #8
|
| 107 | +//! |
| 108 | +//! |
| 109 | +//! $ arm-none-eabi-size -Ax $(find target -name app) | head |
| 110 | +//! target/thumbv7m-none-eabi/debug/app : |
| 111 | +//! section size addr |
| 112 | +//! .vector_table 0x400 0x8000000 |
| 113 | +//! .text 0x24a 0x8000400 |
| 114 | +//! .rodata 0x0 0x800064c |
| 115 | +//! .stack 0x2000 0x20000000 |
| 116 | +//! .bss 0x0 0x20000000 |
| 117 | +//! .data 0x0 0x20000000 |
| 118 | +//! ``` |
| 119 | +//! |
| 120 | +//! ## Zero cost stack overflow protection |
| 121 | +//! |
| 122 | +//! Consider the following variation of the previous program: |
| 123 | +//! |
| 124 | +//! ``` ignore |
| 125 | +//! extern crate cortex_m_rt; |
| 126 | +//! |
| 127 | +//! const N: usize = 256; |
| 128 | +//! static mut XS: [u32; N] = [0; N]; |
| 129 | +//! |
| 130 | +//! fn main() { |
| 131 | +//! #[inline(never)] |
| 132 | +//! fn fib(n: u32) -> u32 { |
| 133 | +//! unsafe { assert!(XS.iter().all(|x| *x == 0)) } |
| 134 | +//! |
| 135 | +//! if n < 2 { |
| 136 | +//! 1 |
| 137 | +//! } else { |
| 138 | +//! fib(n - 1) + fib(n - 2) |
| 139 | +//! } |
| 140 | +//! } |
| 141 | +//! |
| 142 | +//! let x = fib(400); |
| 143 | +//! unsafe { *XS.iter_mut().first().unwrap() = x } |
| 144 | +//! } |
| 145 | +//! ``` |
| 146 | +//! |
| 147 | +//! This program allocates a 1KB array in `.bss`, recursively computes the 400th fibonacci number |
| 148 | +//! and stores the result in the head of the array. This program will hit a stack overflow at |
| 149 | +//! runtime because there's not enough memory to recursively call the `fib` function so many times. |
| 150 | +//! |
| 151 | +//! If you inspect the program using GDB you'll see that the assertion failed after `fib` was nested |
| 152 | +//! around 300 times. |
| 153 | +//! |
| 154 | +//! ``` console |
| 155 | +//! > continue |
| 156 | +//! Program received signal SIGTRAP, Trace/breakpoint trap. |
| 157 | +//! |
| 158 | +//! > backtrace |
| 159 | +//! #0 0x08000516 in cortex_m_rt::default_handler () |
| 160 | +//! #1 <signal handler called> |
| 161 | +//! #2 0x0800050a in rust_begin_unwind () |
| 162 | +//! #3 0x08000586 in core::panicking::panic_fmt () |
| 163 | +//! #4 0x0800055c in core::panicking::panic () |
| 164 | +//! #5 0x080004f6 in app::main::fib () |
| 165 | +//! #6 0x080004a0 in app::main::fib () |
| 166 | +//! (..) |
| 167 | +//! #301 0x080004a0 in app::main::fib () |
| 168 | +//! #302 0x080004a0 in app::main::fib () |
| 169 | +//! #303 0x08000472 in app::main () |
| 170 | +//! #304 0x08000512 in cortex_m_rt::lang_items::start () |
| 171 | +//! #305 0x08000460 in cortex_m_rt::reset_handler () |
105 | 172 | //! ```
|
106 | 173 | //!
|
| 174 | +//! What this means is that the stack grew so much that it crashed into the `.bss` section and |
| 175 | +//! overwrote the memory in there. Continuing the GDB session you can confirm that the `XS` variable |
| 176 | +//! has been modified: |
| 177 | +//! |
| 178 | +//! ``` console |
| 179 | +//! > x/4 0x20000000 # start of .bss |
| 180 | +//! 0x20000000 <app::XS>: 0x00000000 0x00000000 0x00000000 0x00000000 |
| 181 | +//! |
| 182 | +//! > x/4 0x200003f0 # end of .bss |
| 183 | +//! 0x200003f0 <app::XS+1008>: 0x20000400 0x080004f5 0x00000000 0x00000001 |
| 184 | +//! ``` |
| 185 | +//! |
| 186 | +//! The problem is that the stack is growing towards the `.bss` section and both sections overlap as |
| 187 | +//! shown below: |
| 188 | +//! |
| 189 | +//! ``` console |
| 190 | +//! $ arm-none-eabi-size -Ax $(find target -name app) |
| 191 | +//! section size addr |
| 192 | +//! .vector_table 0x400 0x8000000 |
| 193 | +//! .text 0x186 0x8000400 |
| 194 | +//! .rodata 0x50 0x8000590 |
| 195 | +//! .stack 0x2000 0x20000000 |
| 196 | +//! .bss 0x400 0x20000000 |
| 197 | +//! .data 0x0 0x20000400 |
| 198 | +//! ``` |
| 199 | +//! |
| 200 | +//! Graphically the RAM sections look like this: |
| 201 | +//! |
| 202 | +//! <p align="center"> |
| 203 | +//! <img alt="Stack overflow" src="https://i.imgur.com/haJKXr4.png"> |
| 204 | +//! </p> |
| 205 | +//! |
| 206 | +//! To prevent memory corruption due to stack overflows in this scenario it suffices to switch the |
| 207 | +//! sections so that the `.bss` section is near the end of the RAM region and the `.stack` comes |
| 208 | +//! *before* `.bss`, at a lower address. |
| 209 | +//! |
| 210 | +//! To swap the sections you can use the [`cortex-m-rt-ld`] linker to link the program. |
| 211 | +//! |
| 212 | +//! ``` console |
| 213 | +//! $ cargo install cortex-m-rt-ld |
| 214 | +//! |
| 215 | +//! $ xargo rustc --target thumbv7m-none-eabi -- \ |
| 216 | +//! -C link-arg=-Tlink.x -C linker=cortex-m-rt-ld -Z linker-flavor=ld |
| 217 | +//! ``` |
| 218 | +//! |
| 219 | +//! Now you get non overlapping linker sections: |
| 220 | +//! |
| 221 | +//! ``` console |
| 222 | +//! section size addr |
| 223 | +//! .vector_table 0x400 0x8000000 |
| 224 | +//! .text 0x186 0x8000400 |
| 225 | +//! .rodata 0x50 0x8000590 |
| 226 | +//! .stack 0x1c00 0x20000000 |
| 227 | +//! .bss 0x400 0x20001c00 |
| 228 | +//! .data 0x0 0x20002000 |
| 229 | +//! ``` |
| 230 | +//! |
| 231 | +//! Note that the `.stack` section is smaller now. Graphically, the memory layout now looks like |
| 232 | +//! this: |
| 233 | +//! |
| 234 | +//! <p align="center"> |
| 235 | +//! <img alt="Swapped sections" src="https://i.imgur.com/waOKpHw.png"> |
| 236 | +//! </p> |
| 237 | +//! |
| 238 | +//! On stack overflows `.stack` will hit the lower boundary of the RAM region raising a hard fault |
| 239 | +//! exception, instead of silently corrupting the `.bss` section. |
| 240 | +//! |
| 241 | +//! You can confirm this by inspecting the program in GDB. |
| 242 | +//! |
| 243 | +//! ``` console |
| 244 | +//! > continue |
| 245 | +//! Program received signal SIGTRAP, Trace/breakpoint trap. |
| 246 | +//! |
| 247 | +//! > p $sp |
| 248 | +//! $1 = (void *) 0x1ffffff0 |
| 249 | +//! ``` |
| 250 | +//! |
| 251 | +//! The failure mode this time was the `.stack` crashing into the RAM boundary. The variable `XS` is |
| 252 | +//! unaffected this time: |
| 253 | +//! |
| 254 | +//! ``` console |
| 255 | +//! > x/4x app::XS |
| 256 | +//! 0x20001c00 <app::XS>: 0x00000000 0x00000000 0x00000000 0x00000000 |
| 257 | +//! |
| 258 | +//! > x/4x app::XS+252 |
| 259 | +//! 0x20001ff0 <app::XS+1008>: 0x00000000 0x00000000 0x00000000 0x00000000 |
| 260 | +//! ``` |
| 261 | +//! |
| 262 | +//! ## `.heap` |
| 263 | +//! |
| 264 | +//! If your program makes use of a `.heap` section a similar problem can occur: |
| 265 | +//! |
| 266 | +//! <p align="center"> |
| 267 | +//! <img alt="Memory layout when `.heap` exists" src="https://i.imgur.com/kFHRGiF.png"> |
| 268 | +//! </p> |
| 269 | +//! |
| 270 | +//! The `.stack` can crash into the `.heap`, or vice versa, and you'll also get memory corruption. |
| 271 | +//! |
| 272 | +//! `cortex-m-rt-ld` can also be used in this case but the size of the `.heap` section must be |
| 273 | +//! specified via the `_heap_size` symbol in `memory.x`, or in any other linker script. |
| 274 | +//! |
| 275 | +//! ``` console |
| 276 | +//! $ $EDITOR memory.x && tail -n1 $_ |
| 277 | +//! _heap_size = 0x400; |
| 278 | +//! ``` |
| 279 | +//! |
| 280 | +//! ``` console |
| 281 | +//! $ xargo rustc --target thumbv7m-none-eabi -- \ |
| 282 | +//! -C link-arg=-Tlink.x -C linker=cortex-m-rt-ld -Z linker-flavor=ld |
| 283 | +//! |
| 284 | +//! $ arm-none-eabi-size -Ax $(find target -name app) | head |
| 285 | +//! section size addr |
| 286 | +//! .vector_table 0x400 0x8000000 |
| 287 | +//! .text 0x1a8 0x8000400 |
| 288 | +//! .rodata 0x50 0x80005b0 |
| 289 | +//! .stack 0x1800 0x20000000 |
| 290 | +//! .bss 0x400 0x20001800 |
| 291 | +//! .data 0x0 0x20001c00 |
| 292 | +//! .heap 0x400 0x20001c00 |
| 293 | +//! ``` |
| 294 | +//! |
| 295 | +//! Graphically the memory layout looks like this: |
| 296 | +//! |
| 297 | +//! <p align="center"> |
| 298 | +//! <img alt="Swapped sections when `.heap` exists" src="https://i.imgur.com/6Y5DaBp.png"> |
| 299 | +//! </p> |
| 300 | +//! |
| 301 | +//! Now both stack overflows and dynamic memory over-allocations (OOM) will generate hard fault |
| 302 | +//! exceptions, instead of running into each other. |
| 303 | +//! |
107 | 304 | //! # Symbol interfaces
|
108 | 305 | //!
|
109 | 306 | //! This crate makes heavy use of symbols, linker sections and linker scripts to
|
|
213 | 410 | //!
|
214 | 411 | //! Allocating the call stack on a different RAM region.
|
215 | 412 | //!
|
216 |
| -//! ```,ignore |
| 413 | +//! ``` ignore |
217 | 414 | //! MEMORY
|
218 | 415 | //! {
|
219 | 416 | //! /* call stack will go here */
|
|
226 | 423 | //! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM);
|
227 | 424 | //! ```
|
228 | 425 | //!
|
| 426 | +//! ### `_heap_size` |
| 427 | +//! |
| 428 | +//! The size of the `.heap` section. Only meaningful when using `cortex-m-rt-ld`. |
| 429 | +//! |
229 | 430 | //! ### `_stext`
|
230 | 431 | //!
|
231 | 432 | //! This symbol indicates where the `.text` section will be located. If not
|
|
241 | 442 | //!
|
242 | 443 | //! Locate the `.text` section 1024 bytes after the start of the FLASH region.
|
243 | 444 | //!
|
244 |
| -//! ```,ignore |
| 445 | +//! ``` ignore |
245 | 446 | //! _stext = ORIGIN(FLASH) + 0x400;
|
246 | 447 | //! ```
|
247 | 448 | //!
|
|
253 | 454 | //!
|
254 | 455 | //! #### Example
|
255 | 456 | //!
|
256 |
| -//! ```,ignore |
| 457 | +//! ``` ignore |
257 | 458 | //! extern crate some_allocator;
|
258 | 459 | //!
|
259 | 460 | //! // Size of the heap in bytes
|
|
270 | 471 | //! }
|
271 | 472 | //! }
|
272 | 473 | //! ```
|
| 474 | +//! |
| 475 | +//! *NOTE* if you are using `cortex-m-rt-ld` and/or have defined the `_heap_size` symbol then you should |
| 476 | +//! use the address of the `_eheap` to compute the size of the `.heap` section, instead of |
| 477 | +//! duplicating the value that you wrote in `memory.x`. |
| 478 | +//! |
| 479 | +//! [1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html |
| 480 | +//! [qs]: https://docs.rs/cortex-m-quickstart/0.2.0/cortex_m_quickstart/ |
| 481 | +//! [`cortex-m-rt-ld`]: https://crates.io/crates/cortex-m-rt-ld |
| 482 | +//! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html |
273 | 483 |
|
274 | 484 | #![cfg_attr(any(target_arch = "arm", feature = "abort-on-panic"), feature(core_intrinsics))]
|
275 | 485 | #![deny(missing_docs)]
|
|
0 commit comments