Repository: esp-rs/esp-hal (rust)

Issue:
Check config stability
https://docs.espressif.com/projects/rust/esp-hal/1.0.0-beta.0/esp32c6/src/esp_hal/uart.rs.html#244 should be unstable, but we're not marking it as such.

Pull request:
Mark unstable parts of config unstable
## Thank you for your contribution!

We appreciate the time and effort you've put into this pull request.
To help us review it efficiently, please ensure you've gone through the following checklist:

### Submission Checklist 📝
- [ ] I have updated existing examples or added new ones (if applicable).
- [x] I have used `cargo xtask fmt-packages` command to ensure that all changed code is formatted correctly.
- [ ] My changes were added to the [`CHANGELOG.md`](https://github.com/esp-rs/esp-hal/blob/main/esp-hal/CHANGELOG.md) in the **_proper_** section.
- [ ] I have added necessary changes to user code to the [Migration Guide](https://github.com/esp-rs/esp-hal/blob/main/esp-hal/MIGRATING-0.21.md).
- [x] My changes are in accordance to the [esp-rs developer guidelines](https://github.com/esp-rs/esp-hal/blob/main/documentation/DEVELOPER-GUIDELINES.md)

#### Extra:
- [x] I have read the [CONTRIBUTING.md guide](https://github.com/esp-rs/esp-hal/blob/main/documentation/CONTRIBUTING.md) and followed its instructions.

### Pull Request Details 📖

#### Description
Fixes #3208

#### Testing
Generate docs and have a look


Patch:
diff --git a/esp-hal-procmacros/src/builder.rs b/esp-hal-procmacros/src/builder.rs
index 789e4d49..8ec05a1c 100644
--- a/esp-hal-procmacros/src/builder.rs
+++ b/esp-hal-procmacros/src/builder.rs
@@ -25,6 +25,8 @@ const KNOWN_HELPERS: &[&str] = &[
     "skip_setter",
     // Do not generate a getter
     "skip_getter",
+    // Feature gate the generated setters and getters by the "unstable" feature
+    "unstable",
 ];
 
 pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
@@ -53,6 +55,15 @@ pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
             continue;
         }
 
+        let unstable = if helper_attributes.iter().any(|h| h == "unstable") {
+            quote! {
+                #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
+                #[cfg(feature = "unstable")]
+            }
+        } else {
+            quote! {}
+        };
+
         let field_ident = field.ident.as_ref().unwrap();
         let field_type = &field.ty;
 
@@ -86,6 +97,7 @@ pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
         if !helper_attributes.iter().any(|h| h == "skip_setter") {
             fns.push(quote! {
                 #[doc = concat!(" Assign the given value to the `", stringify!(#field_ident) ,"` field.")]
+                #unstable
                 #[must_use]
                 pub fn #function_ident(mut self, #field_ident: #field_setter_type) -> Self {
                     self.#field_ident = #field_assigns;
@@ -97,6 +109,7 @@ pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
                 let function_ident = format_ident!("with_{}_none", field_ident);
                 fns.push(quote! {
                     #[doc = concat!(" Set the value of `", stringify!(#field_ident), "` to `None`.")]
+                    #unstable
                     #[must_use]
                     pub fn #function_ident(mut self) -> Self {
                         self.#field_ident = None;
@@ -121,6 +134,7 @@ pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
             });
             fns.push(quote! {
                 #(#docs)*
+                #unstable
                 pub fn #field_ident(&self) -> #field_type {
                     self.#field_ident
                 }
diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md
index adf6f374..9825689e 100644
--- a/esp-hal/CHANGELOG.md
+++ b/esp-hal/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 
 - Support for `rand_core` 0.9 (#3211)
+- `ESP_HAL_CONFIG_STACK_GUARD_OFFSET` and `ESP_HAL_CONFIG_STACK_GUARD_VALUE` to configure Rust's [Stack smashing protection](https://doc.rust-lang.org/rustc/exploit-mitigations.html#stack-smashing-protection) (#3203)
 
 ### Changed
 
@@ -22,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - ESP32-S2: Fixed PSRAM initialization (#3196)
 - `Uart::{with_tx, with_rx}` can now be called on the async driver as well (#3212)
 - ESP32: Fixed SPI3 QSPI signals (#3201)
+- ESP32-C6/H2: The `flip_link` feature should no longer crash (#3203)
 
 ### Removed
 
diff --git a/esp-hal/build.rs b/esp-hal/build.rs
index 2ff56888..6abb749c 100644
--- a/esp-hal/build.rs
+++ b/esp-hal/build.rs
@@ -1,4 +1,5 @@
 use std::{
+    collections::HashMap,
     env,
     error::Error,
     fs::{self, File},
@@ -122,6 +123,19 @@ fn main() -> Result<(), Box<dyn Error>> {
             Some(Validator::Enumeration(
                 vec![String::from("quad"), String::from("octal")]
             )),
+        ),
+        // Rust's stack smashing protection configuration
+        (
+            "stack-guard-offset",
+            "The stack guard variable will be placed this many bytes from the stack's end.",
+            Value::Integer(4096),
+            None
+        ),
+        (
+            "stack-guard-value",
+            "The value to be written to the stack guard variable.",
+            Value::Integer(0xDEED_BAAD),
+            None
         )
     ], true);
 
@@ -166,10 +180,21 @@ fn main() -> Result<(), Box<dyn Error>> {
     } else {
         // RISC-V devices:
 
-        preprocess_file(&config_symbols, "ld/riscv/asserts.x", out.join("asserts.x"))?;
-        preprocess_file(&config_symbols, "ld/riscv/debug.x", out.join("debug.x"))?;
         preprocess_file(
             &config_symbols,
+            &cfg,
+            "ld/riscv/asserts.x",
+            out.join("asserts.x"),
+        )?;
+        preprocess_file(
+            &config_symbols,
+            &cfg,
+            "ld/riscv/debug.x",
+            out.join("debug.x"),
+        )?;
+        preprocess_file(
+            &config_symbols,
+            &cfg,
             "ld/riscv/hal-defaults.x",
             out.join("hal-defaults.x"),
         )?;
@@ -177,8 +202,8 @@ fn main() -> Result<(), Box<dyn Error>> {
 
     // With the architecture-specific linker scripts taken care of, we can copy all
     // remaining linker scripts which are common to all devices:
-    copy_dir_all(&config_symbols, "ld/sections", &out)?;
-    copy_dir_all(&config_symbols, format!("ld/{device_name}"), &out)?;
+    copy_dir_all(&config_symbols, &cfg, "ld/sections", &out)?;
+    copy_dir_all(&config_symbols, &cfg, format!("ld/{device_name}"), &out)?;
 
     Ok(())
 }
@@ -188,6 +213,7 @@ fn main() -> Result<(), Box<dyn Error>> {
 
 fn copy_dir_all(
     config_symbols: &[&str],
+    cfg: &HashMap<String, Value>,
     src: impl AsRef<Path>,
     dst: impl AsRef<Path>,
 ) -> std::io::Result<()> {
@@ -198,12 +224,14 @@ fn copy_dir_all(
         if ty.is_dir() {
             copy_dir_all(
                 config_symbols,
+                cfg,
                 entry.path(),
                 dst.as_ref().join(entry.file_name()),
             )?;
         } else {
             preprocess_file(
                 config_symbols,
+                cfg,
                 entry.path(),
                 dst.as_ref().join(entry.file_name()),
             )?;
@@ -215,9 +243,12 @@ fn copy_dir_all(
 /// A naive pre-processor for linker scripts
 fn preprocess_file(
     config: &[&str],
+    cfg: &HashMap<String, Value>,
     src: impl AsRef<Path>,
     dst: impl AsRef<Path>,
 ) -> std::io::Result<()> {
+    println!("cargo:rerun-if-changed={}", src.as_ref().display());
+
     let file = File::open(src)?;
     let mut out_file = File::create(dst)?;
 
@@ -225,7 +256,7 @@ fn preprocess_file(
     take.push(true);
 
     for line in std::io::BufReader::new(file).lines() {
-        let line = line?;
+        let line = substitute_config(cfg, &line?);
         let trimmed = line.trim();
 
         if let Some(condition) = trimmed.strip_prefix("#IF ") {
@@ -252,6 +283,43 @@ fn preprocess_file(
     Ok(())
 }
 
+fn substitute_config(cfg: &HashMap<String, Value>, line: &str) -> String {
+    let mut result = String::new();
+    let mut chars = line.chars().peekable();
+
+    while let Some(c) = chars.next() {
+        if c != '$' {
+            result.push(c);
+            continue;
+        }
+
+        let Some('{') = chars.peek() else {
+            result.push(c);
+            continue;
+        };
+        chars.next();
+
+        let mut key = String::new();
+        for c in chars.by_ref() {
+            if c == '}' {
+                break;
+            }
+            key.push(c);
+        }
+        match cfg
+            .get(&key)
+            .unwrap_or_else(|| panic!("missing config key: {key}"))
+        {
+            Value::Bool(true) => result.push('1'),
+            Value::Bool(false) => result.push('0'),
+            Value::Integer(value) => result.push_str(&value.to_string()),
+            Value::String(value) => result.push_str(value),
+        }
+    }
+
+    result
+}
+
 #[cfg(feature = "esp32")]
 fn generate_memory_extras() -> Vec<u8> {
     let reserve_dram = if cfg!(feature = "bluetooth") {
diff --git a/esp-hal/ld/esp32/esp32.x b/esp-hal/ld/esp32/esp32.x
index a61c7579..cd20b332 100644
--- a/esp-hal/ld/esp32/esp32.x
+++ b/esp-hal/ld/esp32/esp32.x
@@ -19,9 +19,11 @@ INCLUDE "fixups/rtc_fast_rwdata_dummy.x"
 /* END ESP32 fixups */
 
 /* Shared sections - ordering matters */
-INCLUDE "rwtext.x"
+SECTIONS {
+  INCLUDE "rwtext.x"
+  INCLUDE "rwdata.x"
+}
 INCLUDE "text.x"
-INCLUDE "rwdata.x"
 INCLUDE "rodata.x"
 INCLUDE "rtc_fast.x"
 INCLUDE "rtc_slow.x"
diff --git a/esp-hal/ld/esp32c2/esp32c2.x b/esp-hal/ld/esp32c2/esp32c2.x
index 76225573..ab0e041e 100644
--- a/esp-hal/ld/esp32c2/esp32c2.x
+++ b/esp-hal/ld/esp32c2/esp32c2.x
@@ -80,9 +80,11 @@ PROVIDE(__global_pointer$ = _data_start + 0x800);
 /* end of esp32c2 fixups */
 
 /* Shared sections - ordering matters */
-INCLUDE "rwtext.x"
+SECTIONS {
+  INCLUDE "rwtext.x"
+  INCLUDE "rwdata.x"
+}
 INCLUDE "text.x"
-INCLUDE "rwdata.x"
 INCLUDE "rodata.x"
 INCLUDE "stack.x"
 INCLUDE "dram2.x"
diff --git a/esp-hal/ld/esp32c3/esp32c3.x b/esp-hal/ld/esp32c3/esp32c3.x
index 4ed604f9..51fe0dfd 100644
--- a/esp-hal/ld/esp32c3/esp32c3.x
+++ b/esp-hal/ld/esp32c3/esp32c3.x
@@ -80,9 +80,11 @@ PROVIDE(__global_pointer$ = _data_start + 0x800);
 /* end of esp32c3 fixups */
 
 /* Shared sections - ordering matters */
-INCLUDE "rwtext.x"
+SECTIONS {
+  INCLUDE "rwtext.x"
+  INCLUDE "rwdata.x"
+}
 INCLUDE "text.x"
-INCLUDE "rwdata.x"
 INCLUDE "rodata.x"
 INCLUDE "rtc_fast.x"
 INCLUDE "stack.x"
diff --git a/esp-hal/ld/esp32c6/esp32c6.x b/esp-hal/ld/esp32c6/esp32c6.x
index ac3ea162..abb1e11d 100644
--- a/esp-hal/ld/esp32c6/esp32c6.x
+++ b/esp-hal/ld/esp32c6/esp32c6.x
@@ -55,8 +55,17 @@ SECTIONS {
     KEEP(*(.trap));
     *(.trap.*);
   } > RWTEXT
+
+  /* Shared sections - ordering matters */
+  INCLUDE "rwtext.x"
+  INCLUDE "rwdata.x"
+  /* End of Shared sections */
 }
-INSERT BEFORE .rwtext;
+#IF ESP_HAL_CONFIG_FLIP_LINK
+/* INSERT BEFORE does not seem to work for the .stack section. Instead, we place every RAM
+  section after .stack if `flip_link` is enabled. */
+INSERT AFTER .stack;
+#ENDIF
 
 SECTIONS {
   /**
@@ -71,15 +80,13 @@ SECTIONS {
 INSERT BEFORE .rodata;
 /* end of esp32c6 fixups */
 
-/* Shared sections - ordering matters */
-INCLUDE "rwtext.x"
+/* Shared sections #2 - ordering matters */
 INCLUDE "text.x"
-INCLUDE "rwdata.x"
 INCLUDE "rodata.x"
 INCLUDE "rtc_fast.x"
 INCLUDE "stack.x"
 INCLUDE "dram2.x"
-/* End of Shared sections */
+/* End of Shared sections #2 */
 
 INCLUDE "debug.x"
 
diff --git a/esp-hal/ld/esp32h2/esp32h2.x b/esp-hal/ld/esp32h2/esp32h2.x
index d4ce0dbe..b00b8514 100644
--- a/esp-hal/ld/esp32h2/esp32h2.x
+++ b/esp-hal/ld/esp32h2/esp32h2.x
@@ -43,14 +43,24 @@ PROVIDE(_start_trap = default_start_trap);
 /* Must be called __global_pointer$ for linker relaxations to work. */
 PROVIDE(__global_pointer$ = _data_start + 0x800);
 
+
 SECTIONS {
   .trap : ALIGN(4)
   {
     KEEP(*(.trap));
     *(.trap.*);
   } > RWTEXT
+
+  /* Shared sections - ordering matters */
+  INCLUDE "rwtext.x"
+  INCLUDE "rwdata.x"
+  /* End of Shared sections */
 }
-INSERT BEFORE .rwtext;
+#IF ESP_HAL_CONFIG_FLIP_LINK
+/* INSERT BEFORE does not seem to work for the .stack section. Instead, we place every RAM
+  section after .stack if `flip_link` is enabled. */
+INSERT AFTER .stack;
+#ENDIF
 
 SECTIONS {
   /**
@@ -64,15 +74,13 @@ SECTIONS {
 }
 INSERT BEFORE .rodata;
 
-/* Shared sections - ordering matters */
-INCLUDE "rwtext.x"
+/* Shared sections #2 - ordering matters */
 INCLUDE "text.x"
-INCLUDE "rwdata.x"
 INCLUDE "rodata.x"
 INCLUDE "rtc_fast.x"
 INCLUDE "stack.x"
 INCLUDE "dram2.x"
-/* End of Shared sections */
+/* End of Shared sections #2 */
 
 INCLUDE "debug.x"
 
diff --git a/esp-hal/ld/esp32s2/esp32s2.x b/esp-hal/ld/esp32s2/esp32s2.x
index 83869824..4b0a7f6d 100644
--- a/esp-hal/ld/esp32s2/esp32s2.x
+++ b/esp-hal/ld/esp32s2/esp32s2.x
@@ -27,9 +27,11 @@ INCLUDE "fixups/rtc_fast_rwdata_dummy.x"
 /* End of fixups for esp32s2 */
 
 /* Shared sections - ordering matters */
-INCLUDE "rwtext.x"
+SECTIONS {
+  INCLUDE "rwtext.x"
+  INCLUDE "rwdata.x"
+}
 INCLUDE "text.x"
-INCLUDE "rwdata.x"
 INCLUDE "rodata.x"
 INCLUDE "rtc_fast.x"
 INCLUDE "rtc_slow.x"
diff --git a/esp-hal/ld/esp32s3/esp32s3.x b/esp-hal/ld/esp32s3/esp32s3.x
index a9f130ee..108a7ce6 100644
--- a/esp-hal/ld/esp32s3/esp32s3.x
+++ b/esp-hal/ld/esp32s3/esp32s3.x
@@ -41,9 +41,11 @@ INCLUDE "fixups/rodata_dummy.x"
 /* End of ESP32S3 fixups */
 
 /* Shared sections - ordering matters */
-INCLUDE "rwtext.x"
+SECTIONS {
+  INCLUDE "rwtext.x"
+  INCLUDE "rwdata.x"
+}
 INCLUDE "text.x"
-INCLUDE "rwdata.x"
 INCLUDE "rodata.x"
 INCLUDE "rtc_fast.x"
 INCLUDE "rtc_slow.x"
diff --git a/esp-hal/ld/sections/rwdata.x b/esp-hal/ld/sections/rwdata.x
index f6accabe..1655a9fd 100644
--- a/esp-hal/ld/sections/rwdata.x
+++ b/esp-hal/ld/sections/rwdata.x
@@ -1,63 +1,59 @@
-
-
-SECTIONS {
-  .data : ALIGN(4)
-  {
-    _data_start = ABSOLUTE(.);
-    . = ALIGN (4);
-
-    #IF ESP_HAL_CONFIG_PLACE_SWITCH_TABLES_IN_RAM
-      *(.rodata.*_esp_hal_internal_handler*)
-      *(.rodata..Lswitch.table.*)
-      *(.rodata.cst*)
-    #ENDIF
-
-    #IF ESP_HAL_CONFIG_PLACE_ANON_IN_RAM
-      *(.rodata..Lanon .rodata..Lanon.*)
-    #ENDIF
-
-    *(.sdata .sdata.* .sdata2 .sdata2.*);
-    *(.data .data.*);
-    *(.data1)
-    _data_end = ABSOLUTE(.);
-    . = ALIGN(4);
-  } > RWDATA AT > RODATA
-
-  /* LMA of .data */
-  _sidata = LOADADDR(.data);
-
-  .bss (NOLOAD) : ALIGN(4)
-  {
-    _bss_start = ABSOLUTE(.);
-    . = ALIGN (4);
-    *(.dynsbss)
-    *(.sbss)
-    *(.sbss.*)
-    *(.gnu.linkonce.sb.*)
-    *(.scommon)
-    *(.sbss2)
-    *(.sbss2.*)
-    *(.gnu.linkonce.sb2.*)
-    *(.dynbss)
-    *(.sbss .sbss.* .bss .bss.*);
-    *(.share.mem)
-    *(.gnu.linkonce.b.*)
-    *(COMMON)
-    _bss_end = ABSOLUTE(.);
-    . = ALIGN(4);
-  } > RWDATA
-
-  .noinit (NOLOAD) : ALIGN(4)
-  {
-    . = ALIGN(4);
-    *(.noinit .noinit.*)
-    . = ALIGN(4);
-  } > RWDATA
-
-  .data.wifi :
-  {
-    . = ALIGN(4);
-    *( .dram1 .dram1.*)
-    . = ALIGN(4);
-  } > RWDATA AT > RODATA
-}
\ No newline at end of file
+.data : ALIGN(4)
+{
+  _data_start = ABSOLUTE(.);
+  . = ALIGN (4);
+
+  #IF ESP_HAL_CONFIG_PLACE_SWITCH_TABLES_IN_RAM
+    *(.rodata.*_esp_hal_internal_handler*)
+    *(.rodata..Lswitch.table.*)
+    *(.rodata.cst*)
+  #ENDIF
+
+  #IF ESP_HAL_CONFIG_PLACE_ANON_IN_RAM
+    *(.rodata..Lanon .rodata..Lanon.*)
+  #ENDIF
+
+  *(.sdata .sdata.* .sdata2 .sdata2.*);
+  *(.data .data.*);
+  *(.data1)
+  _data_end = ABSOLUTE(.);
+  . = ALIGN(4);
+} > RWDATA AT > RODATA
+
+/* LMA of .data */
+_sidata = LOADADDR(.data);
+
+.bss (NOLOAD) : ALIGN(4)
+{
+  _bss_start = ABSOLUTE(.);
+  . = ALIGN (4);
+  *(.dynsbss)
+  *(.sbss)
+  *(.sbss.*)
+  *(.gnu.linkonce.sb.*)
+  *(.scommon)
+  *(.sbss2)
+  *(.sbss2.*)
+  *(.gnu.linkonce.sb2.*)
+  *(.dynbss)
+  *(.sbss .sbss.* .bss .bss.*);
+  *(.share.mem)
+  *(.gnu.linkonce.b.*)
+  *(COMMON)
+  _bss_end = ABSOLUTE(.);
+  . = ALIGN(4);
+} > RWDATA
+
+.noinit (NOLOAD) : ALIGN(4)
+{
+  . = ALIGN(4);
+  *(.noinit .noinit.*)
+  . = ALIGN(4);
+} > RWDATA
+
+.data.wifi :
+{
+  . = ALIGN(4);
+  *( .dram1 .dram1.*)
+  . = ALIGN(4);
+} > RWDATA AT > RODATA
diff --git a/esp-hal/ld/sections/rwtext.x b/esp-hal/ld/sections/rwtext.x
index aca9385b..e1dbf589 100644
--- a/esp-hal/ld/sections/rwtext.x
+++ b/esp-hal/ld/sections/rwtext.x
@@ -1,22 +1,20 @@
-SECTIONS {
-  .rwtext : ALIGN(4)
-  {
-    . = ALIGN (4);
-    *(.rwtext.literal .rwtext .rwtext.literal.* .rwtext.*)
-    . = ALIGN(4);
-  } > RWTEXT
+.rwtext : ALIGN(4)
+{
+  . = ALIGN (4);
+  *(.rwtext.literal .rwtext .rwtext.literal.* .rwtext.*)
+  . = ALIGN(4);
+} > RWTEXT
 
-  .rwtext.wifi :
-  {
-    . = ALIGN(4);
-    *( .wifi0iram  .wifi0iram.*)
-    *( .wifirxiram  .wifirxiram.*)
-    *( .wifislprxiram  .wifislprxiram.*)
-    *( .wifislpiram  .wifislpiram.*)
-    *( .phyiram  .phyiram.*)
-    *( .iram1  .iram1.*)
-    *( .wifiextrairam.* )
-    *( .coexiram.* )
-    . = ALIGN(4);
-  } > RWTEXT AT > RODATA
-}
\ No newline at end of file
+.rwtext.wifi :
+{
+  . = ALIGN(4);
+  *( .wifi0iram  .wifi0iram.*)
+  *( .wifirxiram  .wifirxiram.*)
+  *( .wifislprxiram  .wifislprxiram.*)
+  *( .wifislpiram  .wifislpiram.*)
+  *( .phyiram  .phyiram.*)
+  *( .iram1  .iram1.*)
+  *( .wifiextrairam.* )
+  *( .coexiram.* )
+  . = ALIGN(4);
+} > RWTEXT AT > RODATA
diff --git a/esp-hal/ld/sections/stack.x b/esp-hal/ld/sections/stack.x
index cfb3695c..90050968 100644
--- a/esp-hal/ld/sections/stack.x
+++ b/esp-hal/ld/sections/stack.x
@@ -1,5 +1,3 @@
-#IF ESP_HAL_CONFIG_FLIP_LINK
-/* no Xtensa chip is supported - so we can assume RISC-V */
 SECTIONS {
   /* must be last segment using RWDATA */
   .stack (NOLOAD) : ALIGN(4)
@@ -7,37 +5,19 @@ SECTIONS {
     _stack_end = ABSOLUTE(.);
     _stack_end_cpu0 = ABSOLUTE(.);
 
+    /* The stack_guard for `stack-protector` mitigation - https://doc.rust-lang.org/rustc/exploit-mitigations.html#stack-smashing-protection */
+    __stack_chk_guard = _stack_end + ${ESP_HAL_CONFIG_STACK_GUARD_OFFSET};
+
+/* no Xtensa chip is supported - so we can assume RISC-V */
+#IF ESP_HAL_CONFIG_FLIP_LINK
     /* Since we cannot know how much the alignment padding of the sections will add we shrink the stack for "the worst case"
     */
     . = . + LENGTH(RWDATA) -  (SIZEOF(.trap) + SIZEOF(.rwtext) + SIZEOF(.rwtext.wifi) + SIZEOF(.data) + SIZEOF(.bss) + SIZEOF(.noinit) + SIZEOF(.data.wifi)) - 304;
-
+#ELSE
+    . = ORIGIN(RWDATA) + LENGTH(RWDATA);
+#ENDIF
     . = ALIGN (4);
     _stack_start = ABSOLUTE(.);
     _stack_start_cpu0 = ABSOLUTE(.);
   } > RWDATA
 }
-INSERT BEFORE .trap;
-
-#ELSE
-
-SECTIONS {
-  /* must be last segment using RWDATA */
-  .stack (NOLOAD) : ALIGN(4)
-  {
-    . = ALIGN (4);
-    _stack_end = ABSOLUTE(.);
-    _stack_end_cpu0 = ABSOLUTE(.);
-  } > RWDATA
-}
-
-PROVIDE(_stack_start = ORIGIN(RWDATA) + LENGTH(RWDATA));
-PROVIDE(_stack_start_cpu0 = ORIGIN(RWDATA) + LENGTH(RWDATA));
-
-#ENDIF
-
-/*
-Provide the stack_guard for `stack-protector`
-
-Ideally the offset should be configurable - should be done once we have https://github.com/esp-rs/esp-hal/issues/1111
-*/
-PROVIDE(__stack_chk_guard = _stack_end + 4096);
diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs
index 86700aa5..a56d1880 100644
--- a/esp-hal/src/lib.rs
+++ b/esp-hal/src/lib.rs
@@ -312,15 +312,51 @@ WARNING: use --release
   peripherals and/or devices.
 "}
 
-/// A marker trait for initializing drivers in a specific mode.
+/// A marker trait for driver modes.
+///
+/// Different driver modes offer different features and different API. Using
+/// this trait as a generic parameter ensures that the driver is initialized in
+/// the correct mode.
 pub trait DriverMode: crate::private::Sealed {}
 
-/// Driver initialized in blocking mode.
+/// Marker type signalling that a driver is initialized in blocking mode.
+///
+/// Drivers are constructed in blocking mode by default. To learn about the
+/// differences between blocking and async drivers, see the [`Async`] mode
+/// documentation.
 #[derive(Debug)]
+#[non_exhaustive]
 pub struct Blocking;
 
-/// Driver initialized in async mode.
+/// Marker type signalling that a driver is initialized in async mode.
+///
+/// Drivers are constructed in blocking mode by default. To set up an async
+/// driver, a [`Blocking`] driver must be converted to an `Async` driver using
+/// the `into_async` method. Drivers can be converted back to blocking mode
+/// using the `into_blocking` method.
+///
+/// Async mode drivers offer most of the same features as blocking drivers, but
+/// with the addition of async APIs. Interrupt-related functions are not
+/// available in async mode, as they are handled by the driver's interrupt
+/// handlers.
+///
+/// Note that async functions usually take up more space than their blocking
+/// counterparts, and they are generally slower. This is because async functions
+/// are implemented using a state machine that is driven by interrupts and is
+/// polled by a runtime. For short operations, the overhead of the state machine
+/// can be significant. Consider using the blocking functions on the async
+/// driver for small transfers.
+///
+/// When initializing an async driver, the driver disables user-specified
+/// interrupt handlers, and sets up internal interrupt handlers that drive the
+/// driver's async API. The driver's interrupt handlers run on the same core as
+/// the driver was initialized on. This means that the driver can not be sent
+/// across threads, to prevent incorrect concurrent access to the peripheral.
+///
+/// Switching back to blocking mode will disable the interrupt handlers and
+/// return the driver to a state where it can be sent across threads.
 #[derive(Debug)]
+#[non_exhaustive]
 pub struct Async(PhantomData<*const ()>);
 
 unsafe impl Sync for Async {}
@@ -418,7 +454,10 @@ fn hal_main(a0: usize, a1: usize, a2: usize) -> ! {
         let stack_chk_guard = core::ptr::addr_of_mut!(__stack_chk_guard);
         // we _should_ use a random value but we don't have a good source for random
         // numbers here
-        stack_chk_guard.write_volatile(0xdeadbabe);
+        stack_chk_guard.write_volatile(esp_config::esp_config_int!(
+            u32,
+            "ESP_HAL_CONFIG_STACK_GUARD_VALUE"
+        ));
 
         main(a0, a1, a2);
     }
diff --git a/esp-hal/src/soc/esp32/mod.rs b/esp-hal/src/soc/esp32/mod.rs
index 535e0f63..89f590f5 100644
--- a/esp-hal/src/soc/esp32/mod.rs
+++ b/esp-hal/src/soc/esp32/mod.rs
@@ -114,7 +114,10 @@ pub unsafe extern "C" fn ESP32Reset() -> ! {
         let stack_chk_guard = core::ptr::addr_of_mut!(__stack_chk_guard);
         // we _should_ use a random value but we don't have a good source for random
         // numbers here
-        stack_chk_guard.write_volatile(0xdeadbabe);
+        stack_chk_guard.write_volatile(esp_config::esp_config_int!(
+            u32,
+            "ESP_HAL_CONFIG_STACK_GUARD_VALUE"
+        ));
     }
 
     crate::interrupt::setup_interrupts();
diff --git a/esp-hal/src/soc/esp32s2/mod.rs b/esp-hal/src/soc/esp32s2/mod.rs
index 812645df..d9ac0097 100644
--- a/esp-hal/src/soc/esp32s2/mod.rs
+++ b/esp-hal/src/soc/esp32s2/mod.rs
@@ -118,7 +118,10 @@ pub unsafe extern "C" fn ESP32Reset() -> ! {
         let stack_chk_guard = core::ptr::addr_of_mut!(__stack_chk_guard);
         // we _should_ use a random value but we don't have a good source for random
         // numbers here
-        stack_chk_guard.write_volatile(0xdeadbabe);
+        stack_chk_guard.write_volatile(esp_config::esp_config_int!(
+            u32,
+            "ESP_HAL_CONFIG_STACK_GUARD_VALUE"
+        ));
     }
 
     crate::interrupt::setup_interrupts();
diff --git a/esp-hal/src/soc/esp32s3/mod.rs b/esp-hal/src/soc/esp32s3/mod.rs
index 8f602661..598b03cf 100644
--- a/esp-hal/src/soc/esp32s3/mod.rs
+++ b/esp-hal/src/soc/esp32s3/mod.rs
@@ -157,7 +157,10 @@ pub unsafe extern "C" fn ESP32Reset() -> ! {
         let stack_chk_guard = core::ptr::addr_of_mut!(__stack_chk_guard);
         // we _should_ use a random value but we don't have a good source for random
         // numbers here
-        stack_chk_guard.write_volatile(0xdeadbabe);
+        stack_chk_guard.write_volatile(esp_config::esp_config_int!(
+            u32,
+            "ESP_HAL_CONFIG_STACK_GUARD_VALUE"
+        ));
     }
 
     crate::interrupt::setup_interrupts();
diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs
index 4464bf16..7c76bee9 100644
--- a/esp-hal/src/spi/master.rs
+++ b/esp-hal/src/spi/master.rs
@@ -449,7 +449,7 @@ pub struct Config {
     frequency: Rate,
 
     /// The clock source
-    #[cfg_attr(not(feature = "unstable"), builder_lite(skip))]
+    #[builder_lite(unstable)]
     #[builder_lite(skip_setter)]
     clock_source: ClockSource,
 
diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs
index 2b2deb3a..25e00629 100644
--- a/esp-hal/src/uart.rs
+++ b/esp-hal/src/uart.rs
@@ -242,6 +242,7 @@ pub struct Config {
     baudrate: u32,
     /// Determines how close to the desired baud rate value the driver should
     /// set the baud rate.
+    #[builder_lite(unstable)]
     baudrate_tolerance: BaudrateTolerance,
     /// Number of data bits in each frame (5, 6, 7, or 8 bits).
     data_bits: DataBits,
@@ -250,7 +251,7 @@ pub struct Config {
     /// Number of stop bits in each frame (1, 1.5, or 2 bits).
     stop_bits: StopBits,
     /// Clock source used by the UART peripheral.
-    #[cfg_attr(not(feature = "unstable"), builder_lite(skip))]
+    #[builder_lite(unstable)]
     clock_source: ClockSource,
     /// UART Receive part configuration.
     rx: RxConfig,


Test patch:
diff --git a/hil-test/.cargo/config.toml b/hil-test/.cargo/config.toml
index 68552882..7a54a18e 100644
--- a/hil-test/.cargo/config.toml
+++ b/hil-test/.cargo/config.toml
@@ -1,9 +1,9 @@
 [target.'cfg(target_arch = "riscv32")']
 runner    = "probe-rs run --preverify"
 rustflags = [
-    "-C", "link-arg=-Tlinkall.x",
     "-C", "link-arg=-Tembedded-test.x",
     "-C", "link-arg=-Tdefmt.x",
+    "-C", "link-arg=-Tlinkall.x",
     "-C", "force-frame-pointers"
 ]
 
@@ -11,9 +11,9 @@ rustflags = [
 runner    = "probe-rs run --preverify"
 rustflags = [
     "-C", "link-arg=-nostartfiles",
-    "-C", "link-arg=-Wl,-Tlinkall.x",
-    "-C", "link-arg=-Tdefmt.x",
     "-C", "link-arg=-Tembedded-test.x",
+    "-C", "link-arg=-Tdefmt.x",
+    "-C", "link-arg=-Wl,-Tlinkall.x",
 ]
 
 [env]
diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml
index 6e4a843f..92ad00ae 100644
--- a/hil-test/Cargo.toml
+++ b/hil-test/Cargo.toml
@@ -56,6 +56,10 @@ harness = false
 name    = "get_time"
 harness = false
 
+[[test]]
+name    = "flip_link"
+harness = false
+
 [[test]]
 name    = "gpio"
 harness = false
diff --git a/hil-test/tests/flip_link.rs b/hil-test/tests/flip_link.rs
new file mode 100644
index 00000000..58a715c5
--- /dev/null
+++ b/hil-test/tests/flip_link.rs
@@ -0,0 +1,20 @@
+//! Tests flip_link
+
+//% CHIPS: esp32c6 esp32h2
+//% ENV: ESP_HAL_CONFIG_FLIP_LINK = true
+
+#![no_std]
+#![no_main]
+
+use hil_test as _;
+
+#[cfg(test)]
+#[embedded_test::tests(default_timeout = 3)]
+mod tests {
+    #[test]
+    fn test() {
+        let _p = esp_hal::init(Default::default());
+        defmt::info!("Hello, world!");
+        defmt::assert_eq!(1 + 1, 2);
+    }
+}