From 878eb488a48fffff138cd7f8672411705ac0d73b Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 9 Nov 2017 00:55:35 +0100 Subject: [PATCH 1/6] make _sbss override-able, add a fictitious .stack section This is one possible solution to the stack overflow problem described in #34. This approach uses a linker wrapper, called [swap-ld], to generate the desired memory layout. See #34 for a description of the desired memory layout and #41 for a description of how `swap-ld` works. The observable effects of this change in cortex-m programs are: - the `_sbss` symbol is now override-able. - there is now a `.stack` linker section that denotes the span of the call stack. `.stack` won't be loaded into the program; it just exists for informative purposes (`swap-ld` uses this information). Given the following program: ``` rust fn main() { static mut X: u32 = 0; static mut Y: u32 = 1; loop { unsafe { ptr::write_volatile(&mut X, X + 1); ptr::write_volatile(&mut Y, Y + 1); } } } ``` If you link this program using the `arm-none-eabi-ld` linker, which is the cortex-m-quickstart default, you'll get the following memory layout: ``` console $ console section size addr .vector_table 0x130 0x8000000 .text 0x94 0x8000130 .rodata 0x0 0x80001c4 .stack 0x5000 0x20000000 .bss 0x4 0x20000000 .data 0x4 0x20000004 ``` Note how the space reserved for the stack (depicted by the `.stack` linker section) overlaps with the space where .bss and .data reside. If you, instead, link this program using `swap-ld` you'll get the following memory layout: ``` console $ arm-none-eabi-size -Ax app section size addr .vector_table 0x130 0x8000000 .text 0x94 0x8000130 .rodata 0x0 0x80001c4 .stack 0x4ff8 0x20000000 .bss 0x4 0x20004ff8 .data 0x4 0x20004ffc ``` Note that no overlap exists in this case and that the call stack size has reduced to accommodate the .bss and .data sections. Unlike #41 the addresses of static variables is now correct: ``` console $ arm-none-eabi-objdump -CD app Disassembly of section .vector_table: 08000000 <_svector_table>: 8000000: 20004ff8 strdcs r4, [r0], -r8 ; initial Stack Pointer 08000004 : 8000004: 08000131 stmdaeq r0, {r0, r4, r5, r8} 08000008 : 8000008: 080001bd stmdaeq r0, {r0, r2, r3, r4, r5, r7, r8} (..) Disassembly of section .stack: 20000000 <.stack>: ... Disassembly of section .bss: 20004ff8 : 20004ff8: 00000000 andeq r0, r0, r0 Disassembly of section .data: 20004ffc <_sdata>: 20004ffc: 00000001 andeq r0, r0, r1 ``` closes #34 [swap-ld]: https://github.com/japaric/swap-ld --- link.x | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/link.x b/link.x index 2386cb0d..2f4b0db4 100644 --- a/link.x +++ b/link.x @@ -48,9 +48,19 @@ SECTIONS . = ALIGN(4); } > FLASH - .bss : ALIGN(4) + /* limits of the .stack region */ + _sstack = ORIGIN(RAM); + _estack = _stack_start; + + /* ficticious region that represents the memory available for the stack */ + .stack _sstack (INFO) : ALIGN(4) + { + . += (_estack - _sstack); + } + + PROVIDE(_sbss = ORIGIN(RAM)); + .bss _sbss : ALIGN(4) { - _sbss = .; *(.bss .bss.*); . = ALIGN(4); _ebss = .; From d9f2e69fcee77e452ed1489bd9d94439d5f30589 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 22 Nov 2017 04:40:04 +0100 Subject: [PATCH 2/6] .heap support This adds a fictitious `.heap` linker section that denotes the span of a heap region. The size of this linker section is governed by the `_heap_size` symbol which defaults to 0 but can be overridden by the user. --- link.x | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/link.x b/link.x index 2f4b0db4..ceea3071 100644 --- a/link.x +++ b/link.x @@ -52,7 +52,7 @@ SECTIONS _sstack = ORIGIN(RAM); _estack = _stack_start; - /* ficticious region that represents the memory available for the stack */ + /* fictitious region that represents the memory available for the stack */ .stack _sstack (INFO) : ALIGN(4) { . += (_estack - _sstack); @@ -75,6 +75,17 @@ SECTIONS _edata = .; } > RAM AT > FLASH + PROVIDE(_heap_size = 0); + + _sheap = _edata; + _eheap = _sheap + _heap_size; + + /* fictitious region that represents the memory available for the heap */ + .heap _sheap (INFO) : ALIGN(4) + { + . += _heap_size; + } + /* fake output .got section */ /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in the input files and raise an error if relocatable code From 7198cd0d07ea150bea2db26f3e0c641d6b5109a9 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 24 Nov 2017 23:02:38 +0100 Subject: [PATCH 3/6] hack to continue supporting two RAM regions --- link.x | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/link.x b/link.x index ceea3071..4eaa8bea 100644 --- a/link.x +++ b/link.x @@ -49,8 +49,12 @@ SECTIONS } > FLASH /* limits of the .stack region */ - _sstack = ORIGIN(RAM); _estack = _stack_start; + /* HACK the `true` case indicates that two RAM regions are being used and + /* that the stack was placed in the second region. In that case we don't know + /* the size of the second RAM region, or its start address, so we just assume + /* its zero sized */ + _sstack = _stack_start < ORIGIN(RAM)? _stack_start : ORIGIN(RAM); /* fictitious region that represents the memory available for the stack */ .stack _sstack (INFO) : ALIGN(4) From deadbab798c8b4555eb026c3be7bcc5fe0b61daf Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 24 Nov 2017 23:20:36 +0100 Subject: [PATCH 4/6] fix rustdoc warnings --- src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index ff5db497..37c6566a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -270,6 +270,10 @@ //! } //! } //! ``` +//! +//! [1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html +//! [qs]: https://docs.rs/cortex-m-quickstart/0.2.0/cortex_m_quickstart/ +//! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html #![cfg_attr(any(target_arch = "arm", feature = "abort-on-panic"), feature(core_intrinsics))] #![deny(missing_docs)] From 114a7087325216ed97666519fb3e6e04814e3624 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 25 Nov 2017 05:47:13 +0100 Subject: [PATCH 5/6] update documentation --- src/lib.rs | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 213 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 37c6566a..4bc0570b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,8 @@ //! //! - A `_sheap` symbol at whose address you can locate a heap. //! +//! - Zero cost stack overflow protection when using the `swap-ld` linker. +//! //! # Example //! //! Creating a new bare metal project. (I recommend you use the @@ -41,13 +43,13 @@ //! $ cargo new --bin app && cd $_ //! //! $ # add this crate as a dependency -//! $ edit Cargo.toml && cat $_ +//! $ $EDITOR Cargo.toml && tail $_ //! [dependencies.cortex-m-rt] //! features = ["abort-on-panic"] //! version = "0.3.0" //! //! $ # tell Xargo which standard crates to build -//! $ edit Xargo.toml && cat $_ +//! $ $EDITOR Xargo.toml && cat $_ //! [dependencies.core] //! stage = 0 //! @@ -56,7 +58,7 @@ //! stage = 1 //! //! $ # memory layout of the device -//! $ edit memory.x && cat $_ +//! $ $EDITOR memory.x && cat $_ //! MEMORY //! { //! /* NOTE K = KiBi = 1024 bytes */ @@ -64,7 +66,7 @@ //! RAM : ORIGIN = 0x20000000, LENGTH = 8K //! } //! -//! $ edit src/main.rs && cat $_ +//! $ $EDITOR src/main.rs && cat $_ //! ``` //! //! ``` ignore,no_run @@ -102,8 +104,203 @@ //! 8000400: b580 push {r7, lr} //! 8000402: 466f mov r7, sp //! 8000404: b084 sub sp, #8 +//! +//! +//! $ arm-none-eabi-size -Ax $(find target -name app) | head +//! target/thumbv7m-none-eabi/debug/app : +//! section size addr +//! .vector_table 0x400 0x8000000 +//! .text 0x24a 0x8000400 +//! .rodata 0x0 0x800064c +//! .stack 0x2000 0x20000000 +//! .bss 0x0 0x20000000 +//! .data 0x0 0x20000000 +//! ``` +//! +//! ## Zero stack overflow protection +//! +//! Consider the following variation of the previous program: +//! +//! ``` ignore +//! extern crate cortex_m_rt; +//! +//! const N: usize = 256; +//! static mut XS: [u32; N] = [0; N]; +//! +//! fn main() { +//! #[inline(never)] +//! fn fib(n: u32) -> u32 { +//! unsafe { assert!(XS.iter().all(|x| *x == 0)) } +//! +//! if n < 2 { +//! 1 +//! } else { +//! fib(n - 1) + fib(n - 2) +//! } +//! } +//! +//! let x = fib(400); +//! unsafe { *XS.iter_mut().first().unwrap() = x } +//! } +//! ``` +//! +//! This program allocates a 1KB array in `.bss`, recursively computes the 400th fibonacci number +//! and stores the result in the head of the array. This program will hit a stack overflow at +//! runtime because there's not enough memory to recursively call the `fib` function so many times. +//! +//! If you inspect the program using GDB you'll see that the assertion failed after `fib` was nested +//! around 300 times. +//! +//! ``` console +//! > continue +//! Program received signal SIGTRAP, Trace/breakpoint trap. +//! +//! > backtrace +//! #0 0x08000516 in cortex_m_rt::default_handler () +//! #1 +//! #2 0x0800050a in rust_begin_unwind () +//! #3 0x08000586 in core::panicking::panic_fmt () +//! #4 0x0800055c in core::panicking::panic () +//! #5 0x080004f6 in app::main::fib () +//! #6 0x080004a0 in app::main::fib () +//! (..) +//! #301 0x080004a0 in app::main::fib () +//! #302 0x080004a0 in app::main::fib () +//! #303 0x08000472 in app::main () +//! #304 0x08000512 in cortex_m_rt::lang_items::start () +//! #305 0x08000460 in cortex_m_rt::reset_handler () +//! ``` +//! +//! What this means is that the stack grew so much that it crashed into the `.bss` section and +//! overwrote the memory in there. Continuing the GDB session you can confirm that the `XS` variable +//! has been modified: +//! +//! ``` console +//! > x/4 0x20000000 # start of .bss +//! 0x20000000 : 0x00000000 0x00000000 0x00000000 0x00000000 +//! +//! > x/4 0x200003f0 # end of .bss +//! 0x200003f0 : 0x20000400 0x080004f5 0x00000000 0x00000001 +//! ``` +//! +//! The problem is that the stack is growing towards the `.bss` section and both sections overlap as +//! shown below: +//! +//! ``` console +//! $ arm-none-eabi-size -Ax $(find target -name app) +//! section size addr +//! .vector_table 0x400 0x8000000 +//! .text 0x186 0x8000400 +//! .rodata 0x50 0x8000590 +//! .stack 0x2000 0x20000000 +//! .bss 0x400 0x20000000 +//! .data 0x0 0x20000400 +//! ``` +//! +//! Graphically the RAM sections look like this: +//! +//!

+//! Stack overflow +//!

+//! +//! To prevent memory corruption due to stack overflows in this scenario it suffices to switch the +//! sections so that the `.bss` section is near the end of the RAM region and the `.stack` comes +//! *before* `.bss`, at a lower address. +//! +//! To swap the sections you can use the [`swap-ld`] linker to link the program. +//! +//! ``` console +//! $ cargo install swap-ld +//! +//! $ xargo rustc --target thumbv7m-none-eabi -- \ +//! -C link-arg=-Tlink.x -C linker=swap-ld -Z linker-flavor=ld //! ``` //! +//! Now you get non overlapping linker sections: +//! +//! ``` console +//! section size addr +//! .vector_table 0x400 0x8000000 +//! .text 0x186 0x8000400 +//! .rodata 0x50 0x8000590 +//! .stack 0x1c00 0x20000000 +//! .bss 0x400 0x20001c00 +//! .data 0x0 0x20002000 +//! ``` +//! +//! Note that the `.stack` section is smaller now. Graphically, the memory layout now looks like +//! this: +//! +//!

+//! Swapped sections +//!

+//! +//! On stack overflows `.stack` will hit the lower boundary of the RAM region raising a hard fault +//! exception, instead of silently corrupting the `.bss` section. +//! +//! You can confirm this by inspecting the program in GDB. +//! +//! ``` console +//! > continue +//! Program received signal SIGTRAP, Trace/breakpoint trap. +//! +//! > p $sp +//! $1 = (void *) 0x1ffffff0 +//! ``` +//! +//! The failure mode this time was the `.stack` crashing into the RAM boundary. The variable `XS` is +//! unaffected this time: +//! +//! ``` console +//! > x/4x app::XS +//! 0x20001c00 : 0x00000000 0x00000000 0x00000000 0x00000000 +//! +//! > x/4x app::XS+252 +//! 0x20001ff0 : 0x00000000 0x00000000 0x00000000 0x00000000 +//! ``` +//! +//! ## `.heap` +//! +//! If your program makes use of a `.heap` section a similar problem can occur: +//! +//!

+//! Memory layout when `.heap` exists +//!

+//! +//! The `.stack` can crash into the `.heap`, or vice versa, and you'll also get memory corruption. +//! +//! `swap-ld` can also be used in this case but the size of the `.heap` section must be specified +//! in the `_heap_size` symbol in `memory.x`, or in any other linker script. +//! +//! ``` console +//! $ $EDITOR memory.x && tail $_ +//! _heap_size = 0x400; +//! ``` +//! +//! ``` console +//! $ xargo rustc --target thumbv7m-none-eabi -- \ +//! -C link-arg=-Tlink.x -C linker=swap-ld -Z linker-flavor=ld +//! +//! $ arm-none-eabi-size -Ax $(find target -name app) | head +//! section size addr +//! .vector_table 0x400 0x8000000 +//! .text 0x1a8 0x8000400 +//! .rodata 0x50 0x80005b0 +//! .stack 0x1800 0x20000000 +//! .bss 0x400 0x20001800 +//! .data 0x0 0x20001c00 +//! .heap 0x400 0x20001c00 +//! ``` +//! +//! Graphically the memory layout looks like this: +//! +//!

+//! Swapped sections when `.heap` exists +//!

+//! +//! Now both stack overflows and dynamic memory over-allocations will generate hard fault +//! exceptions, instead of running into each other. +//! //! # Symbol interfaces //! //! This crate makes heavy use of symbols, linker sections and linker scripts to @@ -213,7 +410,7 @@ //! //! Allocating the call stack on a different RAM region. //! -//! ```,ignore +//! ``` ignore //! MEMORY //! { //! /* call stack will go here */ @@ -226,6 +423,10 @@ //! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM); //! ``` //! +//! ### `_heap_size` +//! +//! The size of the `.heap` section. Only meaningful when using `swap-ld`. +//! //! ### `_stext` //! //! This symbol indicates where the `.text` section will be located. If not @@ -241,7 +442,7 @@ //! //! Locate the `.text` section 1024 bytes after the start of the FLASH region. //! -//! ```,ignore +//! ``` ignore //! _stext = ORIGIN(FLASH) + 0x400; //! ``` //! @@ -253,7 +454,7 @@ //! //! #### Example //! -//! ```,ignore +//! ``` ignore //! extern crate some_allocator; //! //! // Size of the heap in bytes @@ -271,8 +472,13 @@ //! } //! ``` //! +//! *NOTE* if you are using `swap-ld` and/or have defined the `_heap_size` symbol then you should +//! use the address of the `_eheap` to compute the size of the `.heap` section, instead of +//! duplicating the value that you wrote in `memory.x`. +//! //! [1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html //! [qs]: https://docs.rs/cortex-m-quickstart/0.2.0/cortex_m_quickstart/ +//! [`swap-ld`]: https://crates.io/crates/swap-ld //! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html #![cfg_attr(any(target_arch = "arm", feature = "abort-on-panic"), feature(core_intrinsics))] From a5350743d0f01fe665cde8995718ab65e861b413 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 17 Feb 2018 17:12:24 +0100 Subject: [PATCH 6/6] s/swap-ld/cortex-m-rt-ld/g also fix a bug when .bss was empty but .data was not --- link.x | 2 +- src/lib.rs | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/link.x b/link.x index 4eaa8bea..6f39655f 100644 --- a/link.x +++ b/link.x @@ -70,7 +70,7 @@ SECTIONS _ebss = .; } > RAM - .data : ALIGN(4) + .data _ebss : ALIGN(4) { _sidata = LOADADDR(.data); _sdata = .; diff --git a/src/lib.rs b/src/lib.rs index 4bc0570b..cb85f4ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,7 @@ //! //! - A `_sheap` symbol at whose address you can locate a heap. //! -//! - Zero cost stack overflow protection when using the `swap-ld` linker. +//! - Zero cost stack overflow protection when using the `cortex-m-rt-ld` linker. //! //! # Example //! @@ -117,7 +117,7 @@ //! .data 0x0 0x20000000 //! ``` //! -//! ## Zero stack overflow protection +//! ## Zero cost stack overflow protection //! //! Consider the following variation of the previous program: //! @@ -207,13 +207,13 @@ //! sections so that the `.bss` section is near the end of the RAM region and the `.stack` comes //! *before* `.bss`, at a lower address. //! -//! To swap the sections you can use the [`swap-ld`] linker to link the program. +//! To swap the sections you can use the [`cortex-m-rt-ld`] linker to link the program. //! //! ``` console -//! $ cargo install swap-ld +//! $ cargo install cortex-m-rt-ld //! //! $ xargo rustc --target thumbv7m-none-eabi -- \ -//! -C link-arg=-Tlink.x -C linker=swap-ld -Z linker-flavor=ld +//! -C link-arg=-Tlink.x -C linker=cortex-m-rt-ld -Z linker-flavor=ld //! ``` //! //! Now you get non overlapping linker sections: @@ -269,17 +269,17 @@ //! //! The `.stack` can crash into the `.heap`, or vice versa, and you'll also get memory corruption. //! -//! `swap-ld` can also be used in this case but the size of the `.heap` section must be specified -//! in the `_heap_size` symbol in `memory.x`, or in any other linker script. +//! `cortex-m-rt-ld` can also be used in this case but the size of the `.heap` section must be +//! specified via the `_heap_size` symbol in `memory.x`, or in any other linker script. //! //! ``` console -//! $ $EDITOR memory.x && tail $_ +//! $ $EDITOR memory.x && tail -n1 $_ //! _heap_size = 0x400; //! ``` //! //! ``` console //! $ xargo rustc --target thumbv7m-none-eabi -- \ -//! -C link-arg=-Tlink.x -C linker=swap-ld -Z linker-flavor=ld +//! -C link-arg=-Tlink.x -C linker=cortex-m-rt-ld -Z linker-flavor=ld //! //! $ arm-none-eabi-size -Ax $(find target -name app) | head //! section size addr @@ -298,7 +298,7 @@ //! Swapped sections when `.heap` exists //!

//! -//! Now both stack overflows and dynamic memory over-allocations will generate hard fault +//! Now both stack overflows and dynamic memory over-allocations (OOM) will generate hard fault //! exceptions, instead of running into each other. //! //! # Symbol interfaces @@ -425,7 +425,7 @@ //! //! ### `_heap_size` //! -//! The size of the `.heap` section. Only meaningful when using `swap-ld`. +//! The size of the `.heap` section. Only meaningful when using `cortex-m-rt-ld`. //! //! ### `_stext` //! @@ -472,13 +472,13 @@ //! } //! ``` //! -//! *NOTE* if you are using `swap-ld` and/or have defined the `_heap_size` symbol then you should +//! *NOTE* if you are using `cortex-m-rt-ld` and/or have defined the `_heap_size` symbol then you should //! use the address of the `_eheap` to compute the size of the `.heap` section, instead of //! duplicating the value that you wrote in `memory.x`. //! //! [1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html //! [qs]: https://docs.rs/cortex-m-quickstart/0.2.0/cortex_m_quickstart/ -//! [`swap-ld`]: https://crates.io/crates/swap-ld +//! [`cortex-m-rt-ld`]: https://crates.io/crates/cortex-m-rt-ld //! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html #![cfg_attr(any(target_arch = "arm", feature = "abort-on-panic"), feature(core_intrinsics))]