Nitrokey / nitrokey-3-firmware

Nitrokey 3 firmware
Apache License 2.0
239 stars 23 forks source link

Add stack analysis tool #313

Open sosthene-nitrokey opened 1 year ago

sosthene-nitrokey commented 1 year ago

With -Z emit-stack-sizes we can get the stack sizes of each function in the final firmware. This could be used to analyze stack usage to fix stack-overflows. This could be made into a small util. Currently this issue will be used to document the (hacky) process:

Steps:

  1. use nightly
  2. add to the linker script SECTIONS (need to be added to runners/embedded/ld/cortex-m-rt_0.6.15_link.x):
  /* `INFO` makes the section not allocatable so it won't be loaded into memory */
  .stack_sizes (INFO) :
  {
    KEEP(*(.stack_sizes));
  }
  1. add strip = false to release profile
  2. compile with RUSTFLAGS="-Z emit-stack-sizes" make flash-develop EXTRA_FEATURES=... (from utils/nrf-builder)
  3. use the following script to get the functions (path may need to be adjusted):
#!/usr/bin/env cargo

//! ```cargo
//! [package]
//! edition = "2021"
//! [dependencies]
//! stack-sizes = "0.5.0"
//! ```

use std::fs::read;

use stack_sizes::analyze_executable;
fn main() {
    let path = "../nitrokey-3-firmware/target/thumbv7em-none-eabihf/release/nrf52_runner";
    let data = read(path).unwrap();
    let functions = analyze_executable(&data).unwrap();
    let mut sorted: Vec<_> = functions.defined.into_values().collect();
    sorted.sort_by_key(|f| f.stack().unwrap_or(0));
    println!("{sorted:#?}");
}
sosthene-nitrokey commented 1 year ago

Other tools that can help make the output more readable: c++filt

sosthene-nitrokey commented 1 year ago

Branch stack-sizes contains the hacks used.

szszszsz commented 1 year ago

I've improved output format, env call for +x, and it reads from stdin now.

#!/usr/bin/env -S cargo +nightly -Zscript

//! ```cargo
//! [package]
//! edition = "2021"
//! [dependencies]
//! stack-sizes = "0.5.0"
//!```

use std::io::Read;
use std::io;

use stack_sizes::analyze_executable;
fn main() {
    let mut stdin = io::stdin().lock();
    let mut buffer = Vec::new();
    stdin.read_to_end(&mut buffer).unwrap();
    let functions = analyze_executable(&buffer).unwrap();
    let mut sorted: Vec<_> = functions.defined.into_values().collect();
    sorted.sort_by_key(|f| f.stack().unwrap_or(0));
    for f in sorted {
        println!("{:5?} {:5} {:?}", f.stack(), f.size(), f.names() );
    }
}
robin-nitrokey commented 11 months ago

Next iteration:

#!/usr/bin/env -S cargo +nightly -Zscript

//! ```cargo
//! [package]
//! edition = "2021"
//! [dependencies]
//! stack-sizes = "0.5.0"
//! symbolic = { version = "12.4.1", features = ["demangle"] }
//!```

use std::io::Read;
use std::io;

use stack_sizes::analyze_executable;
use symbolic::demangle;

fn main() {
    let mut stdin = io::stdin().lock();
    let mut buffer = Vec::new();
    stdin.read_to_end(&mut buffer).unwrap();
    let functions = analyze_executable(&buffer).unwrap();
    let mut sorted: Vec<_> = functions.defined.into_values().collect();
    sorted.sort_by_key(|f| f.stack().unwrap_or(0));
    for f in sorted.into_iter().rev() {
        if let Some(stack) = f.stack() {
            print!("{:6}", stack);
        } else {
            print!("None");
        }
        print!(" {:6} ", f.size());
        for (i, name) in f.names().into_iter().enumerate() {
            if i > 0 {
                print!(", ");
            }
            print!("{}", demangle::demangle(name));
        }
        println!();
    }
}

Example output:

108568    652 lpc55_runner::app::rtic_ext::main::__rtic_init_resources
 22920    264 <iso7816::command::Command<_> as core::convert::TryFrom<&[u8]>>::try_from
 22896    176 usbd_ctaphid::pipe::Pipe<Bus>::handle_response
 22320  14032 ctap_types::ctap2::Request::deserialize
 22152   3508 embedded_runner_lib::soc::init::Stage2::next
 21800   6020 OS_EVENT
 21440    408 apdu_dispatch::dispatch::ApduDispatch::handle_reply
 19832  15260 trussed::service::ServiceResources<P>::reply_to
 18064   1048 webcrypt::ctap_app::try_handle_ctap2
 17392    364 webcrypt::ctap_app::try_handle_ctap1