diff --git a/.github/workflows/embedded.yml b/.github/workflows/embedded.yml new file mode 100644 index 0000000..112760b --- /dev/null +++ b/.github/workflows/embedded.yml @@ -0,0 +1,23 @@ +name: Test Embedded + +on: + push: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - name: Checkout Toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: rust-src + target: thumbv7m-none-eabi + - + name: Build + run: cd embedded && cargo build diff --git a/Cargo.toml b/Cargo.toml index 771784f..8167445 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,13 +15,13 @@ name = "bitcoin_hashes" path = "src/lib.rs" [features] -default = [ "std" ] -std = [] +no_std = ["core2"] serde-std = ["serde/std"] unstable = [] # for benchmarking [dependencies] serde = { version = "1.0", default-features = false, optional = true } +core2 = { version = "0.3.0-alpha.1", default-features = false, optional = true } [dev-dependencies] serde_test = "1.0" diff --git a/embedded/.cargo/config b/embedded/.cargo/config new file mode 100644 index 0000000..4cdaced --- /dev/null +++ b/embedded/.cargo/config @@ -0,0 +1,26 @@ +[target.thumbv7m-none-eabi] +# uncomment this to make `cargo run` execute programs on QEMU +runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +rustflags = [ + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", + + # LLD (shipped with the Rust toolchain) is used as the default linker + "-C", "link-arg=-Tlink.x", + + # if you run into problems with LLD switch to the GNU linker by commenting out + # this line + # "-C", "linker=arm-none-eabi-ld", + + # if you need to link to pre-compiled C libraries provided by a C toolchain + # use GCC as the linker by commenting out both lines above and then + # uncommenting the three lines below + # "-C", "linker=arm-none-eabi-gcc", + # "-C", "link-arg=-Wl,-Tlink.x", + # "-C", "link-arg=-nostartfiles", +] + +[build] +target = "thumbv7m-none-eabi" # Cortex-M3 diff --git a/embedded/Cargo.toml b/embedded/Cargo.toml new file mode 100644 index 0000000..508405d --- /dev/null +++ b/embedded/Cargo.toml @@ -0,0 +1,38 @@ +[package] +authors = ["Riccardo Casatta "] +edition = "2018" +readme = "README.md" +name = "embedded" +version = "0.1.0" + +[dependencies] +cortex-m = "0.6.0" +cortex-m-rt = "0.6.10" +cortex-m-semihosting = "0.3.3" +panic-halt = "0.2.0" +bitcoin_hashes = { path="../", features = ["no_std"] } +alloc-cortex-m = "0.4.1" + +# Uncomment for the panic example. +# panic-itm = "0.4.1" + +# Uncomment for the allocator example. +# alloc-cortex-m = "0.4.0" + +# Uncomment for the device example. +# Update `memory.x`, set target to `thumbv7em-none-eabihf` in `.cargo/config`, +# and then use `cargo build --examples device` to build it. +# [dependencies.stm32f3] +# features = ["stm32f303", "rt"] +# version = "0.7.1" + +# this lets you use `cargo fix`! +[[bin]] +name = "embedded" +test = false +bench = false + +[profile.release] +codegen-units = 1 # better optimizations +debug = true # symbols are nice and they don't increase the size on Flash +lto = true # better optimizations diff --git a/embedded/build.rs b/embedded/build.rs new file mode 100644 index 0000000..d534cc3 --- /dev/null +++ b/embedded/build.rs @@ -0,0 +1,31 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/embedded/memory.x b/embedded/memory.x new file mode 100644 index 0000000..b271f22 --- /dev/null +++ b/embedded/memory.x @@ -0,0 +1,34 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* TODO Adjust these memory regions to match your device memory layout */ + /* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */ + FLASH : ORIGIN = 0x00000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 64K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Example of putting non-initialized variables into custom RAM locations. */ +/* This assumes you have defined a region RAM2 above, and in the Rust + sources added the attribute `#[link_section = ".ram2bss"]` to the data + you want to place there. */ +/* Note that the section will not be zero-initialized by the runtime! */ +/* SECTIONS { + .ram2bss (NOLOAD) : ALIGN(4) { + *(.ram2bss); + . = ALIGN(4); + } > RAM2 + } INSERT AFTER .bss; +*/ diff --git a/embedded/src/main.rs b/embedded/src/main.rs new file mode 100644 index 0000000..c723395 --- /dev/null +++ b/embedded/src/main.rs @@ -0,0 +1,56 @@ +#![feature(alloc_error_handler)] +#![no_main] +#![no_std] + +#[macro_use] +extern crate bitcoin_hashes; + +extern crate alloc; +use panic_halt as _; + +use self::alloc::string::ToString; +use self::alloc::vec; +use self::alloc::vec::Vec; +use core::alloc::Layout; + +use alloc_cortex_m::CortexMHeap; +use cortex_m::asm; +use cortex_m_rt::entry; +use cortex_m_semihosting::{debug, hprintln}; + +use bitcoin_hashes::core2::io::Write; +use bitcoin_hashes::sha256; +use bitcoin_hashes::Hash; + +hash_newtype!(TestType, sha256::Hash, 32, doc = "test"); + +// this is the allocator the application will use +#[global_allocator] +static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); + +const HEAP_SIZE: usize = 1024; // in bytes + +#[entry] +fn main() -> ! { + // Initialize the allocator BEFORE you use it + unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) } + + let mut engine = sha256::Hash::engine(); + engine.write_all(&[]).unwrap(); + let hash = sha256::Hash::from_engine(engine); + hprintln!("hash {}", a).unwrap(); + + // exit QEMU + // NOTE do not run this on hardware; it can corrupt OpenOCD state + debug::exit(debug::EXIT_SUCCESS); + + loop {} +} + +// define what happens in an Out Of Memory (OOM) condition +#[alloc_error_handler] +fn alloc_error(_layout: Layout) -> ! { + asm::bkpt(); + + loop {} +} diff --git a/src/hex.rs b/src/hex.rs index 8f3fb9f..ca3ee55 100644 --- a/src/hex.rs +++ b/src/hex.rs @@ -18,6 +18,9 @@ use core::{fmt, str}; use Hash; +#[cfg(all(feature = "no_std", not(test)))] +use alloc::{string::String, format, vec::Vec}; + /// Hex decoding error #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Error { @@ -40,7 +43,6 @@ impl fmt::Display for Error { } /// Trait for objects that can be serialized as hex strings -#[cfg(any(test, feature = "std"))] pub trait ToHex { /// Hex representation of the object fn to_hex(&self) -> String; @@ -60,7 +62,6 @@ pub trait FromHex: Sized { } } -#[cfg(any(test, feature = "std"))] impl ToHex for T { /// Outputs the hash in hexadecimal form fn to_hex(&self) -> String { @@ -174,7 +175,6 @@ pub fn format_hex_reverse(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result { Ok(()) } -#[cfg(any(test, feature = "std"))] impl ToHex for [u8] { fn to_hex(&self) -> String { use core::fmt::Write; @@ -186,7 +186,6 @@ impl ToHex for [u8] { } } -#[cfg(any(test, feature = "std"))] impl FromHex for Vec { fn from_byte_iter(iter: I) -> Result where I: Iterator> + diff --git a/src/std_impls.rs b/src/impls.rs similarity index 94% rename from src/std_impls.rs rename to src/impls.rs index aa87c30..185a66e 100644 --- a/src/std_impls.rs +++ b/src/impls.rs @@ -16,18 +16,25 @@ //! //! impls of traits defined in `std` and not `core` +#[cfg(any(not(feature="no_std"), test))] use std::{error, io}; +#[cfg(all(feature = "no_std", not(test)))] +use core2::io as io; -use {hex, sha1, sha256, sha512, ripemd160, siphash24}; +use {sha1, sha256, sha512, ripemd160, siphash24}; use HashEngine; + +#[cfg(not(feature="no_std"))] use Error; +#[cfg(not(feature="no_std"))] impl error::Error for Error { fn cause(&self) -> Option<&error::Error> { None } fn description(&self) -> &str { "`std::error::description` is deprecated" } } -impl error::Error for hex::Error { +#[cfg(not(feature="no_std"))] +impl error::Error for ::hex::Error { fn cause(&self) -> Option<&error::Error> { None } fn description(&self) -> &str { "`std::error::description` is deprecated" } } diff --git a/src/lib.rs b/src/lib.rs index 9e71c0b..31e1f53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,17 +33,23 @@ #![allow(bare_trait_objects)] #![allow(ellipsis_inclusive_range_patterns)] -#![cfg_attr(all(not(test), not(feature = "std")), no_std)] +#![cfg_attr(all(not(test), feature = "no_std"), no_std)] #![cfg_attr(all(test, feature = "unstable"), feature(test))] #[cfg(all(test, feature = "unstable"))] extern crate test; -#[cfg(any(test, feature="std"))] pub extern crate core; +#[cfg(any(test, not(feature="no_std")))] extern crate core; #[cfg(feature="serde")] pub extern crate serde; #[cfg(all(test,feature="serde"))] extern crate serde_test; +#[cfg(all(feature = "no_std", not(test)))] +extern crate alloc; + +#[cfg(all(feature = "no_std", not(test)))] +pub extern crate core2; + #[macro_use] mod util; #[macro_use] pub mod serde_macros; -#[cfg(any(test, feature = "std"))] mod std_impls; +mod impls; pub mod error; pub mod hex; pub mod hash160; diff --git a/src/util.rs b/src/util.rs index e6d4422..80d109b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -29,8 +29,8 @@ macro_rules! hex_fmt_impl( hex_fmt_impl!($imp, $ty, ); ); ($imp:ident, $ty:ident, $($gen:ident: $gent:ident),*) => ( - impl<$($gen: $gent),*> $crate::core::fmt::$imp for $ty<$($gen),*> { - fn fmt(&self, f: &mut $crate::core::fmt::Formatter) -> $crate::core::fmt::Result { + impl<$($gen: $gent),*> ::core::fmt::$imp for $ty<$($gen),*> { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { use $crate::hex::{format_hex, format_hex_reverse}; if $ty::<$($gen),*>::DISPLAY_BACKWARD { format_hex_reverse(&self.0, f) @@ -49,37 +49,37 @@ macro_rules! index_impl( index_impl!($ty, ); ); ($ty:ident, $($gen:ident: $gent:ident),*) => ( - impl<$($gen: $gent),*> $crate::core::ops::Index for $ty<$($gen),*> { + impl<$($gen: $gent),*> ::core::ops::Index for $ty<$($gen),*> { type Output = u8; fn index(&self, index: usize) -> &u8 { &self.0[index] } } - impl<$($gen: $gent),*> $crate::core::ops::Index<$crate::core::ops::Range> for $ty<$($gen),*> { + impl<$($gen: $gent),*> ::core::ops::Index<::core::ops::Range> for $ty<$($gen),*> { type Output = [u8]; - fn index(&self, index: $crate::core::ops::Range) -> &[u8] { + fn index(&self, index: ::core::ops::Range) -> &[u8] { &self.0[index] } } - impl<$($gen: $gent),*> $crate::core::ops::Index<$crate::core::ops::RangeFrom> for $ty<$($gen),*> { + impl<$($gen: $gent),*> ::core::ops::Index<::core::ops::RangeFrom> for $ty<$($gen),*> { type Output = [u8]; - fn index(&self, index: $crate::core::ops::RangeFrom) -> &[u8] { + fn index(&self, index: ::core::ops::RangeFrom) -> &[u8] { &self.0[index] } } - impl<$($gen: $gent),*> $crate::core::ops::Index<$crate::core::ops::RangeTo> for $ty<$($gen),*> { + impl<$($gen: $gent),*> ::core::ops::Index<::core::ops::RangeTo> for $ty<$($gen),*> { type Output = [u8]; - fn index(&self, index: $crate::core::ops::RangeTo) -> &[u8] { + fn index(&self, index: ::core::ops::RangeTo) -> &[u8] { &self.0[index] } } - impl<$($gen: $gent),*> $crate::core::ops::Index<$crate::core::ops::RangeFull> for $ty<$($gen),*> { + impl<$($gen: $gent),*> ::core::ops::Index<::core::ops::RangeFull> for $ty<$($gen),*> { type Output = [u8]; - fn index(&self, index: $crate::core::ops::RangeFull) -> &[u8] { + fn index(&self, index: ::core::ops::RangeFull) -> &[u8] { &self.0[index] } } @@ -93,19 +93,19 @@ macro_rules! borrow_slice_impl( borrow_slice_impl!($ty, ); ); ($ty:ident, $($gen:ident: $gent:ident),*) => ( - impl<$($gen: $gent),*> $crate::core::borrow::Borrow<[u8]> for $ty<$($gen),*> { + impl<$($gen: $gent),*> ::core::borrow::Borrow<[u8]> for $ty<$($gen),*> { fn borrow(&self) -> &[u8] { &self[..] } } - impl<$($gen: $gent),*> $crate::core::convert::AsRef<[u8]> for $ty<$($gen),*> { + impl<$($gen: $gent),*> ::core::convert::AsRef<[u8]> for $ty<$($gen),*> { fn as_ref(&self) -> &[u8] { &self[..] } } - impl<$($gen: $gent),*> $crate::core::ops::Deref for $ty<$($gen),*> { + impl<$($gen: $gent),*> ::core::ops::Deref for $ty<$($gen),*> { type Target = [u8]; fn deref(&self) -> &Self::Target {