Does Rust currently support unwinding on Cortex-M?

I am looking for a way to unwind on panics on Cortex-M targets, specifically thumbv7em-none-eabi. There are several panic handlers available [1], but no one resembles panic-unwind.

I understand that unwinding needs runtime support. I probably should write my own runtime to unwind. Essential sections, i.e. .eh_frame and .gcc_except_table, must be present in the final output ELF file for the unwinder to proceed. gimli [2] is a useful crate to parse these sections.

However, the above two sections critical to unwinding are not present in my output ELF file when my target is set to thumbv7em-none-eabi. Did I miss any compile option? Or is unwinding simply unsupported on Cortex-M?

You will need to use a target that has panics=unwind instead of panic=abort like

{
  "abi": "eabi",
  "arch": "arm",
  "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
  "emit-debug-gdb-scripts": false,
  "executables": true,
  "frame-pointer": "always",
  "is-builtin": true,
  "linker": "rust-lld",
  "linker-flavor": "ld.lld",
  "llvm-target": "thumbv7em-none-eabi",
  "max-atomic-width": 32,
  "panic-strategy": "abort",
  "relocation-model": "unwind",
  "target-pointer-width": "32"
}

(based on the original target spec for thumbv7em-none-eabi as you can show using rustc +nightly --target thumbv7em-none-eabi -Zunstable-options --print target-spec-json)

You can put this in a .json file and then pass the path to this file using --target instead of thumbv7em-none-eabi.

Next you will need to define the eh_personality lang item. If you are using an existing unwinder, this personality function will be called for every frame and reads .gcc_except_table to determine if unwinding should continue, cleanup should be performed or the panic is catched. If you are using a custom unwinder that doesn't call it but instead directly reads .gcc_except_table you can just make it loop.

#[lang = "eh_personality"]
unsafe extern "C" fn rust_eh_personality() -> ! {
    loop {}
}

This requires nightly and the #![feature(lang_items)] feature gate.

And then finally you need to call your unwinder from #[panic_handler]. The unwinder needs to read .eh_frame to determine how to unwind. Normally the unwinder then calls the personality function for each frame with information like the LSDA which is for GCC and LLVM a pointer into the .gcc_except_table section.

2 Likes

Thank you for your quick answer! But did you inadvertently overwrite "panic-strategy": "unwind" as "relocation-model": "unwind"?

Yes, I did... The right target spec is:

{
  "abi": "eabi",
  "arch": "arm",
  "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
  "emit-debug-gdb-scripts": false,
  "executables": true,
  "frame-pointer": "always",
  "is-builtin": true,
  "linker": "rust-lld",
  "linker-flavor": "ld.lld",
  "llvm-target": "thumbv7em-none-eabi",
  "max-atomic-width": 32,
  "panic-strategy": "unwind",
  "relocation-model": "static",
  "target-pointer-width": "32"
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.