diff --git a/src/Cargo.lock b/src/Cargo.lock index d969370..5618b84 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -35,6 +35,12 @@ version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +[[package]] +name = "cc" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" + [[package]] name = "cfg-if" version = "0.1.10" @@ -76,6 +82,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7313c0d620d0cb4dbd9d019e461a4beb501071ff46ec0ab933efb4daa76d73e3" +[[package]] +name = "dwarf" +version = "0.0.0" +dependencies = [ + "cfg-if", + "compiler_builtins", + "libc", + "unwind", +] + [[package]] name = "dyld" version = "0.1.0" @@ -184,7 +200,7 @@ dependencies = [ [[package]] name = "libasync" version = "0.0.0" -source = "git+https://git.m-labs.hk/M-Labs/zc706.git#ec252b099cf67a51ba9daa72b3292db8c21ba1b8" +source = "git+https://git.m-labs.hk/M-Labs/zc706.git#6195ad40c334a847cd01cd4adadf6b8f8d730908" dependencies = [ "embedded-hal", "libcortex_a9", @@ -196,7 +212,7 @@ dependencies = [ [[package]] name = "libboard_zynq" version = "0.0.0" -source = "git+https://git.m-labs.hk/M-Labs/zc706.git#ec252b099cf67a51ba9daa72b3292db8c21ba1b8" +source = "git+https://git.m-labs.hk/M-Labs/zc706.git#6195ad40c334a847cd01cd4adadf6b8f8d730908" dependencies = [ "bit_field", "embedded-hal", @@ -209,10 +225,14 @@ dependencies = [ "volatile-register", ] +[[package]] +name = "libc" +version = "0.1.0" + [[package]] name = "libcortex_a9" version = "0.0.0" -source = "git+https://git.m-labs.hk/M-Labs/zc706.git#ec252b099cf67a51ba9daa72b3292db8c21ba1b8" +source = "git+https://git.m-labs.hk/M-Labs/zc706.git#6195ad40c334a847cd01cd4adadf6b8f8d730908" dependencies = [ "bit_field", "libregister", @@ -221,7 +241,7 @@ dependencies = [ [[package]] name = "libregister" version = "0.0.0" -source = "git+https://git.m-labs.hk/M-Labs/zc706.git#ec252b099cf67a51ba9daa72b3292db8c21ba1b8" +source = "git+https://git.m-labs.hk/M-Labs/zc706.git#6195ad40c334a847cd01cd4adadf6b8f8d730908" dependencies = [ "bit_field", "vcell", @@ -231,7 +251,7 @@ dependencies = [ [[package]] name = "libsupport_zynq" version = "0.0.0" -source = "git+https://git.m-labs.hk/M-Labs/zc706.git#ec252b099cf67a51ba9daa72b3292db8c21ba1b8" +source = "git+https://git.m-labs.hk/M-Labs/zc706.git#6195ad40c334a847cd01cd4adadf6b8f8d730908" dependencies = [ "compiler_builtins", "libboard_zynq", @@ -364,16 +384,19 @@ dependencies = [ "byteorder", "core_io", "cslice", + "dwarf", "dyld", "fatfs", "futures", "libasync", "libboard_zynq", + "libc", "libcortex_a9", "libsupport_zynq", "log", "num-derive", "num-traits", + "unwind", "void", ] @@ -416,6 +439,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "unwind" +version = "0.0.0" +dependencies = [ + "cc", + "cfg-if", + "compiler_builtins", + "libc", +] + [[package]] name = "vcell" version = "0.1.2" diff --git a/src/Cargo.toml b/src/Cargo.toml index 62e4e11..9a53d0b 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -1,7 +1,10 @@ [workspace] members = [ + "libc", "libdyld", "libcoreio", + "libdwarf", + "libunwind", "runtime", "szl" ] diff --git a/src/armv7-none-eabihf.json b/src/armv7-none-eabihf.json index 4fc452f..a677f3a 100644 --- a/src/armv7-none-eabihf.json +++ b/src/armv7-none-eabihf.json @@ -20,6 +20,8 @@ "max-atomic-width": 32, "os": "none", "panic-strategy": "abort", + "requires-uwtable": true, + "force-unwind-tables": "yes", "relocation-model": "static", "target-c-int-width": "32", "target-endian": "little", diff --git a/src/libc/Cargo.toml b/src/libc/Cargo.toml new file mode 100644 index 0000000..bcc47f2 --- /dev/null +++ b/src/libc/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "libc" +version = "0.1.0" +authors = ["pca006132 "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/libc/src/lib.rs b/src/libc/src/lib.rs new file mode 100644 index 0000000..445b72d --- /dev/null +++ b/src/libc/src/lib.rs @@ -0,0 +1,7 @@ +// Helper crate for dealing with libc dependencies, which only requires these +// definitions... +#![allow(non_camel_case_types)] +#![no_std] +pub type c_int = i32; +pub type uintptr_t = usize; +pub type c_void = core::ffi::c_void; diff --git a/src/libdwarf/Cargo.toml b/src/libdwarf/Cargo.toml new file mode 100644 index 0000000..d5335ea --- /dev/null +++ b/src/libdwarf/Cargo.toml @@ -0,0 +1,17 @@ +[package] +# This package is orignally panic_unwind, but is adapted as a dwarf parser +authors = ["The Rust Project Developers"] +name = "dwarf" +version = "0.0.0" +edition = "2018" + +[lib] +test = false +bench = false +doc = false + +[dependencies] +libc = { path = "../libc" } +unwind = { path = "../libunwind" } +compiler_builtins = "0.1.0" +cfg-if = "0.1.8" diff --git a/src/libdwarf/src/eh.rs b/src/libdwarf/src/eh.rs new file mode 100644 index 0000000..8bb7ff0 --- /dev/null +++ b/src/libdwarf/src/eh.rs @@ -0,0 +1,200 @@ +//! Parsing of GCC-style Language-Specific Data Area (LSDA) +//! For details see: +//! http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html +//! http://mentorembedded.github.io/cxx-abi/exceptions.pdf +//! http://www.airs.com/blog/archives/460 +//! http://www.airs.com/blog/archives/464 +//! +//! A reference implementation may be found in the GCC source tree +//! (`/libgcc/unwind-c.c` as of this writing). + +#![allow(non_upper_case_globals)] +#![allow(unused)] + +use crate::DwarfReader; +use core::mem; + +pub const DW_EH_PE_omit: u8 = 0xFF; +pub const DW_EH_PE_absptr: u8 = 0x00; + +pub const DW_EH_PE_uleb128: u8 = 0x01; +pub const DW_EH_PE_udata2: u8 = 0x02; +pub const DW_EH_PE_udata4: u8 = 0x03; +pub const DW_EH_PE_udata8: u8 = 0x04; +pub const DW_EH_PE_sleb128: u8 = 0x09; +pub const DW_EH_PE_sdata2: u8 = 0x0A; +pub const DW_EH_PE_sdata4: u8 = 0x0B; +pub const DW_EH_PE_sdata8: u8 = 0x0C; + +pub const DW_EH_PE_pcrel: u8 = 0x10; +pub const DW_EH_PE_textrel: u8 = 0x20; +pub const DW_EH_PE_datarel: u8 = 0x30; +pub const DW_EH_PE_funcrel: u8 = 0x40; +pub const DW_EH_PE_aligned: u8 = 0x50; + +pub const DW_EH_PE_indirect: u8 = 0x80; + +#[derive(Copy, Clone)] +pub struct EHContext<'a> { + pub ip: usize, // Current instruction pointer + pub func_start: usize, // Address of the current function + pub get_text_start: &'a dyn Fn() -> usize, // Get address of the code section + pub get_data_start: &'a dyn Fn() -> usize, // Get address of the data section +} + +pub enum EHAction { + None, + Cleanup(usize), + Catch(usize), + Terminate, +} + +pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); + +pub unsafe fn find_eh_action( + lsda: *const u8, + context: &EHContext<'_>, + foreign_exception: bool, +) -> Result { + if lsda.is_null() { + return Ok(EHAction::None); + } + + let func_start = context.func_start; + let mut reader = DwarfReader::new(lsda); + + let start_encoding = reader.read::(); + // base address for landing pad offsets + let lpad_base = if start_encoding != DW_EH_PE_omit { + read_encoded_pointer(&mut reader, context, start_encoding)? + } else { + func_start + }; + + let ttype_encoding = reader.read::(); + if ttype_encoding != DW_EH_PE_omit { + // Rust doesn't analyze exception types, so we don't care about the type table + reader.read_uleb128(); + } + + let call_site_encoding = reader.read::(); + let call_site_table_length = reader.read_uleb128(); + let action_table = reader.ptr.offset(call_site_table_length as isize); + let ip = context.ip; + + if !USING_SJLJ_EXCEPTIONS { + while reader.ptr < action_table { + let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding)?; + let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding)?; + let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding)?; + let cs_action = reader.read_uleb128(); + // Callsite table is sorted by cs_start, so if we've passed the ip, we + // may stop searching. + if ip < func_start + cs_start { + break; + } + if ip < func_start + cs_start + cs_len { + if cs_lpad == 0 { + return Ok(EHAction::None); + } else { + let lpad = lpad_base + cs_lpad; + return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)); + } + } + } + // Ip is not present in the table. This should not happen... but it does: issue #35011. + // So rather than returning EHAction::Terminate, we do this. + Ok(EHAction::None) + } else { + // SjLj version: + // The "IP" is an index into the call-site table, with two exceptions: + // -1 means 'no-action', and 0 means 'terminate'. + match ip as isize { + -1 => return Ok(EHAction::None), + 0 => return Ok(EHAction::Terminate), + _ => (), + } + let mut idx = ip; + loop { + let cs_lpad = reader.read_uleb128(); + let cs_action = reader.read_uleb128(); + idx -= 1; + if idx == 0 { + // Can never have null landing pad for sjlj -- that would have + // been indicated by a -1 call site index. + let lpad = (cs_lpad + 1) as usize; + return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)); + } + } + } +} + +fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) -> EHAction { + if cs_action == 0 { + // If cs_action is 0 then this is a cleanup (Drop::drop). We run these + // for both Rust panics and foreign exceptions. + EHAction::Cleanup(lpad) + } else if foreign_exception { + // catch_unwind should not catch foreign exceptions, only Rust panics. + // Instead just continue unwinding. + EHAction::None + } else { + // Stop unwinding Rust panics at catch_unwind. + EHAction::Catch(lpad) + } +} + +#[inline] +fn round_up(unrounded: usize, align: usize) -> Result { + if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) } +} + +unsafe fn read_encoded_pointer( + reader: &mut DwarfReader, + context: &EHContext<'_>, + encoding: u8, +) -> Result { + if encoding == DW_EH_PE_omit { + return Err(()); + } + + // DW_EH_PE_aligned implies it's an absolute pointer value + if encoding == DW_EH_PE_aligned { + reader.ptr = round_up(reader.ptr as usize, mem::size_of::())? as *const u8; + return Ok(reader.read::()); + } + + let mut result = match encoding & 0x0F { + DW_EH_PE_absptr => reader.read::(), + DW_EH_PE_uleb128 => reader.read_uleb128() as usize, + DW_EH_PE_udata2 => reader.read::() as usize, + DW_EH_PE_udata4 => reader.read::() as usize, + DW_EH_PE_udata8 => reader.read::() as usize, + DW_EH_PE_sleb128 => reader.read_sleb128() as usize, + DW_EH_PE_sdata2 => reader.read::() as usize, + DW_EH_PE_sdata4 => reader.read::() as usize, + DW_EH_PE_sdata8 => reader.read::() as usize, + _ => return Err(()), + }; + + result += match encoding & 0x70 { + DW_EH_PE_absptr => 0, + // relative to address of the encoded value, despite the name + DW_EH_PE_pcrel => reader.ptr as usize, + DW_EH_PE_funcrel => { + if context.func_start == 0 { + return Err(()); + } + context.func_start + } + DW_EH_PE_textrel => (*context.get_text_start)(), + DW_EH_PE_datarel => (*context.get_data_start)(), + _ => return Err(()), + }; + + if encoding & DW_EH_PE_indirect != 0 { + result = *(result as *const usize); + } + + Ok(result) +} diff --git a/src/libdwarf/src/lib.rs b/src/libdwarf/src/lib.rs new file mode 100644 index 0000000..1ce25de --- /dev/null +++ b/src/libdwarf/src/lib.rs @@ -0,0 +1,74 @@ +//! Utilities for parsing DWARF-encoded data streams. +//! See , +//! DWARF-4 standard, Section 7 - "Data Representation" + +#![no_std] +// This module is used only by x86_64-pc-windows-gnu for now, but we +// are compiling it everywhere to avoid regressions. +#![allow(unused)] + +#[cfg(test)] +mod tests; + +pub mod eh; + +use core::mem; + +pub struct DwarfReader { + pub ptr: *const u8, +} + +#[repr(C, packed)] +struct Unaligned(T); + +impl DwarfReader { + pub fn new(ptr: *const u8) -> DwarfReader { + DwarfReader { ptr } + } + + // DWARF streams are packed, so e.g., a u32 would not necessarily be aligned + // on a 4-byte boundary. This may cause problems on platforms with strict + // alignment requirements. By wrapping data in a "packed" struct, we are + // telling the backend to generate "misalignment-safe" code. + pub unsafe fn read(&mut self) -> T { + let Unaligned(result) = *(self.ptr as *const Unaligned); + self.ptr = self.ptr.add(mem::size_of::()); + result + } + + // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable + // Length Data". + pub unsafe fn read_uleb128(&mut self) -> u64 { + let mut shift: usize = 0; + let mut result: u64 = 0; + let mut byte: u8; + loop { + byte = self.read::(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + result + } + + pub unsafe fn read_sleb128(&mut self) -> i64 { + let mut shift: usize = 0; + let mut result: u64 = 0; + let mut byte: u8; + loop { + byte = self.read::(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + // sign-extend + if shift < 8 * mem::size_of::() && (byte & 0x40) != 0 { + result |= (!0 as u64) << shift; + } + result as i64 + } +} diff --git a/src/libdwarf/src/tests.rs b/src/libdwarf/src/tests.rs new file mode 100644 index 0000000..1644f37 --- /dev/null +++ b/src/libdwarf/src/tests.rs @@ -0,0 +1,19 @@ +use super::*; + +#[test] +fn dwarf_reader() { + let encoded: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 0xE5, 0x8E, 0x26, 0x9B, 0xF1, 0x59, 0xFF, 0xFF]; + + let mut reader = DwarfReader::new(encoded.as_ptr()); + + unsafe { + assert!(reader.read::() == u8::to_be(1u8)); + assert!(reader.read::() == u16::to_be(0x0203)); + assert!(reader.read::() == u32::to_be(0x04050607)); + + assert!(reader.read_uleb128() == 624485); + assert!(reader.read_sleb128() == -624485); + + assert!(reader.read::() == i8::to_be(-1)); + } +} diff --git a/src/libdyld/src/lib.rs b/src/libdyld/src/lib.rs index f496dbf..1396947 100644 --- a/src/libdyld/src/lib.rs +++ b/src/libdyld/src/lib.rs @@ -3,7 +3,7 @@ extern crate alloc; extern crate log; -use core::{ops::Range, convert, fmt, str}; +use core::{convert, fmt, str}; use alloc::string::String; use log::{debug, trace}; use elf::*; @@ -57,10 +57,43 @@ fn elf_hash(name: &[u8]) -> u32 { h } +// linker symbols +extern "C" { + #[no_mangle] + static __text_start: u32; + #[no_mangle] + static __text_end: u32; + #[no_mangle] + static __exidx_start: u32; + #[no_mangle] + static __exidx_end: u32; +} + +static mut KERNEL_EXIDX_START: u32 = 0; +static mut KERNEL_EXIDX_END: u32 = 0; + +#[no_mangle] +extern fn dl_unwind_find_exidx(pc: u32, len_ptr: *mut u32) -> u32 { + let length: u32; + let start: u32; + unsafe { + if (&__text_start as *const u32 as u32) <= pc && pc < (&__text_end as *const u32 as u32) { + length = (&__exidx_end - &__exidx_start) as u32; + start = &__exidx_start as *const u32 as u32; + } else { + // make sure that the kernel is loaded + assert_ne!(KERNEL_EXIDX_START, 0); + length = (KERNEL_EXIDX_END - KERNEL_EXIDX_START) / core::mem::size_of::() as u32; + start = KERNEL_EXIDX_START; + } + *len_ptr = length; + } + start +} + pub struct Library { pub image: Image, dyn_section: DynamicSection, - exidx: Range, } impl Library { @@ -130,10 +163,6 @@ impl Library { Ok(self.strtab().get(offset..offset + size) .ok_or("cannot read symbol name")?) } - - pub fn exidx(&self) -> &[usize] { - self.image.get_ref_slice_unchecked(&self.exidx) - } } pub fn load( @@ -169,7 +198,6 @@ pub fn load( .map_err(|_| "cannot allocate target image")?; debug!("ELF target: {} bytes, align to {:X}, allocated at {:08X}", image_size, image_align, image.ptr() as usize); - let mut exidx = None; // LOAD for phdr in file.program_headers() { let phdr = phdr.ok_or("cannot read program header")?; @@ -188,8 +216,13 @@ pub fn load( dst.copy_from_slice(src); } PT_ARM_EXIDX => { - exidx = Some(phdr.p_vaddr as usize.. - (phdr.p_vaddr + phdr.p_filesz) as usize); + let range = image.get(phdr.p_vaddr as usize.. + (phdr.p_vaddr + phdr.p_filesz) as usize) + .ok_or("program header requests and out of bounds load (in target)")?; + unsafe { + KERNEL_EXIDX_START = range.as_ptr() as u32; + KERNEL_EXIDX_END = range.as_ptr().add(range.len()) as u32; + } } _ => {} } @@ -203,8 +236,7 @@ pub fn load( dyn_section.rela.len(), dyn_section.rel.len(), dyn_section.pltrel.len()); let lib = Library { image, - dyn_section, - exidx: exidx.ok_or("missing EXIDX program header")?, + dyn_section }; for rela in lib.rela() { diff --git a/src/libunwind/Cargo.toml b/src/libunwind/Cargo.toml new file mode 100644 index 0000000..346268a --- /dev/null +++ b/src/libunwind/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = ["The Rust Project Developers"] +name = "unwind" +version = "0.0.0" +build = "build.rs" +edition = "2018" +include = [ + '/libunwind/*', +] + +[lib] +name = "unwind" +path = "lib.rs" +test = false +bench = false +doc = false + +[dependencies] +compiler_builtins = "0.1.0" +cfg-if = "0.1.8" +libc = { path = "../libc" } + +[build-dependencies] +cc = { version = "1.0.1" } + +[features] +llvm-libunwind = [] diff --git a/src/libunwind/backtrace.rs b/src/libunwind/backtrace.rs new file mode 100644 index 0000000..7ad0dd7 --- /dev/null +++ b/src/libunwind/backtrace.rs @@ -0,0 +1,41 @@ +use libc::{c_void, c_int}; +use crate::libunwind as uw; + +const UW_REG_SP: c_int = 13; + +pub fn backtrace(f: F) -> Result<(), uw::_Unwind_Reason_Code> + where F: FnMut(usize) -> () +{ + struct TraceContext { + step_fn: F, + prev_sp: uw::_Unwind_Word + } + + extern fn trace(context: *mut uw::_Unwind_Context, arg: *mut c_void) + -> uw::_Unwind_Reason_Code + where F: FnMut(usize) -> () + { + unsafe { + let trace_context = &mut *(arg as *mut TraceContext); + + // Detect the root of a libfringe thread + let cur_sp = uw::_Unwind_GetGR(context, UW_REG_SP); + if cur_sp == trace_context.prev_sp { + return uw::_URC_END_OF_STACK + } else { + trace_context.prev_sp = cur_sp; + } + + (trace_context.step_fn)(uw::_Unwind_GetIP(context)); + uw::_URC_NO_REASON + } + } + + unsafe { + let mut trace_context = TraceContext { step_fn: f, prev_sp: 0 }; + match uw::_Unwind_Backtrace(trace::, &mut trace_context as *mut _ as *mut c_void) { + uw::_URC_NO_REASON => Ok(()), + err => Err(err) + } + } +} diff --git a/src/libunwind/build.rs b/src/libunwind/build.rs new file mode 100644 index 0000000..9966d8d --- /dev/null +++ b/src/libunwind/build.rs @@ -0,0 +1,62 @@ +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + llvm_libunwind::compile(); +} + +mod llvm_libunwind { + use std::path::Path; + + /// Compile the libunwind C/C++ source code. + pub fn compile() { + let cfg = &mut cc::Build::new(); + cfg.cpp(true); + cfg.cpp_set_stdlib(None); + cfg.warnings(false); + + // libunwind expects a __LITTLE_ENDIAN__ macro to be set for LE archs, cf. #65765 + cfg.define("__LITTLE_ENDIAN__", Some("1")); + // still have problem compiling the libunwind + cfg.flag("-nostdlib"); + cfg.flag("-ffreestanding"); + cfg.flag("-fno-PIC"); + cfg.flag("-Isrc"); + cfg.flag("-I../include"); + cfg.flag("-fno-stack-protector"); + cfg.flag("--target=armv7-none-eabihf"); + cfg.define("_LIBUNWIND_IS_BAREMETAL", Some("1")); + cfg.define("_LIBUNWIND_NO_HEAP", Some("1")); + cfg.define("_POSIX_READER_WRITER_LOCKS", Some("1")); + cfg.define("NDEBUG", Some("1")); + + cfg.flag("-std=c99"); + cfg.flag("-std=c++11"); + cfg.flag("-nostdinc++"); + cfg.flag("-fno-exceptions"); + cfg.flag("-fno-rtti"); + cfg.flag("-fstrict-aliasing"); + cfg.flag("-funwind-tables"); + cfg.flag("-fvisibility=hidden"); + cfg.flag_if_supported("-fvisibility-global-new-delete-hidden"); + cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); + + let unwind_sources = vec![ + "Unwind-EHABI.cpp", + "Unwind-seh.cpp", + "Unwind-sjlj.c", + "UnwindLevel1-gcc-ext.c", + "UnwindLevel1.c", + "UnwindRegistersRestore.S", + "UnwindRegistersSave.S", + "libunwind.cpp", + "printf.c", + ]; + + let root = Path::new("../llvm_libunwind"); + cfg.include(root.join("include")); + for src in unwind_sources { + cfg.file(root.join("src").join(src)); + } + + cfg.compile("unwind"); + } +} diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs new file mode 100644 index 0000000..34ae38d --- /dev/null +++ b/src/libunwind/lib.rs @@ -0,0 +1,33 @@ +#![no_std] +#![feature(link_cfg)] +#![feature(nll)] +#![feature(unwind_attributes)] +#![feature(static_nobundle)] +#![cfg_attr(not(target_env = "msvc"), feature(libc))] + +cfg_if::cfg_if! { + if #[cfg(target_env = "msvc")] { + // no extra unwinder support needed + } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { + // no unwinder on the system! + } else { + mod libunwind; + pub use libunwind::*; + mod backtrace; + pub use backtrace::backtrace; + } +} + +#[cfg(target_env = "musl")] +#[link(name = "unwind", kind = "static", cfg(target_feature = "crt-static"))] +#[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] +extern "C" {} + +#[cfg(target_os = "redox")] +#[link(name = "gcc_eh", kind = "static-nobundle", cfg(target_feature = "crt-static"))] +#[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] +extern "C" {} + +#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] +#[link(name = "unwind", kind = "static-nobundle")] +extern "C" {} diff --git a/src/libunwind/libunwind.rs b/src/libunwind/libunwind.rs new file mode 100644 index 0000000..24fce7d --- /dev/null +++ b/src/libunwind/libunwind.rs @@ -0,0 +1,285 @@ +#![allow(nonstandard_style)] + +use libc::{c_int, c_void, uintptr_t}; + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum _Unwind_Reason_Code { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, + _URC_FAILURE = 9, // used only by ARM EHABI +} +pub use _Unwind_Reason_Code::*; + +pub type _Unwind_Exception_Class = u64; +pub type _Unwind_Word = uintptr_t; +pub type _Unwind_Ptr = uintptr_t; +pub type _Unwind_Trace_Fn = + extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code; + +#[cfg(target_arch = "x86")] +pub const unwinder_private_data_size: usize = 5; + +#[cfg(target_arch = "x86_64")] +pub const unwinder_private_data_size: usize = 6; + +#[cfg(all(target_arch = "arm", not(target_os = "ios")))] +pub const unwinder_private_data_size: usize = 20; + +#[cfg(all(target_arch = "arm", target_os = "ios"))] +pub const unwinder_private_data_size: usize = 5; + +#[cfg(target_arch = "aarch64")] +pub const unwinder_private_data_size: usize = 2; + +#[cfg(target_arch = "mips")] +pub const unwinder_private_data_size: usize = 2; + +#[cfg(target_arch = "mips64")] +pub const unwinder_private_data_size: usize = 2; + +#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] +pub const unwinder_private_data_size: usize = 2; + +#[cfg(target_arch = "s390x")] +pub const unwinder_private_data_size: usize = 2; + +#[cfg(target_arch = "sparc64")] +pub const unwinder_private_data_size: usize = 2; + +#[cfg(target_arch = "riscv64")] +pub const unwinder_private_data_size: usize = 2; + +#[cfg(target_os = "emscripten")] +pub const unwinder_private_data_size: usize = 20; + +#[cfg(all(target_arch = "hexagon", target_os = "linux"))] +pub const unwinder_private_data_size: usize = 35; + +#[repr(C)] +pub struct _Unwind_Exception { + pub exception_class: _Unwind_Exception_Class, + pub exception_cleanup: _Unwind_Exception_Cleanup_Fn, + pub private: [_Unwind_Word; unwinder_private_data_size], +} + +pub enum _Unwind_Context {} + +pub type _Unwind_Exception_Cleanup_Fn = + extern "C" fn(unwind_code: _Unwind_Reason_Code, exception: *mut _Unwind_Exception); +#[cfg_attr( + all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")), + link(name = "unwind", kind = "static") +)] +extern "C" { + #[unwind(allowed)] + pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; + pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); + pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void; + pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr; + pub fn _Unwind_GetTextRelBase(ctx: *mut _Unwind_Context) -> _Unwind_Ptr; + pub fn _Unwind_GetDataRelBase(ctx: *mut _Unwind_Context) -> _Unwind_Ptr; +} + +cfg_if::cfg_if! { +if #[cfg(all(any(target_os = "ios", target_os = "netbsd", not(target_arch = "arm"))))] { + // Not ARM EHABI + #[repr(C)] + #[derive(Copy, Clone, PartialEq)] + pub enum _Unwind_Action { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16, + } + pub use _Unwind_Action::*; + + #[cfg_attr(all(feature = "llvm-libunwind", + any(target_os = "fuchsia", target_os = "linux")), + link(name = "unwind", kind = "static"))] + extern "C" { + pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word; + pub fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word); + pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> _Unwind_Word; + pub fn _Unwind_SetIP(ctx: *mut _Unwind_Context, value: _Unwind_Word); + pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, ip_before_insn: *mut c_int) + -> _Unwind_Word; + pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void; + } + +} else { + // ARM EHABI + #[repr(C)] + #[derive(Copy, Clone, PartialEq)] + pub enum _Unwind_State { + _US_VIRTUAL_UNWIND_FRAME = 0, + _US_UNWIND_FRAME_STARTING = 1, + _US_UNWIND_FRAME_RESUME = 2, + _US_ACTION_MASK = 3, + _US_FORCE_UNWIND = 8, + _US_END_OF_STACK = 16, + } + pub use _Unwind_State::*; + + #[repr(C)] + enum _Unwind_VRS_Result { + _UVRSR_OK = 0, + _UVRSR_NOT_IMPLEMENTED = 1, + _UVRSR_FAILED = 2, + } + #[repr(C)] + enum _Unwind_VRS_RegClass { + _UVRSC_CORE = 0, + _UVRSC_VFP = 1, + _UVRSC_FPA = 2, + _UVRSC_WMMXD = 3, + _UVRSC_WMMXC = 4, + } + use _Unwind_VRS_RegClass::*; + #[repr(C)] + enum _Unwind_VRS_DataRepresentation { + _UVRSD_UINT32 = 0, + _UVRSD_VFPX = 1, + _UVRSD_FPAX = 2, + _UVRSD_UINT64 = 3, + _UVRSD_FLOAT = 4, + _UVRSD_DOUBLE = 5, + } + use _Unwind_VRS_DataRepresentation::*; + + pub const UNWIND_POINTER_REG: c_int = 12; + pub const UNWIND_SP_REG: c_int = 13; + pub const UNWIND_IP_REG: c_int = 15; + + #[cfg_attr(all(feature = "llvm-libunwind", + any(target_os = "fuchsia", target_os = "linux")), + link(name = "unwind", kind = "static"))] + extern "C" { + fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, + regclass: _Unwind_VRS_RegClass, + regno: _Unwind_Word, + repr: _Unwind_VRS_DataRepresentation, + data: *mut c_void) + -> _Unwind_VRS_Result; + + fn _Unwind_VRS_Set(ctx: *mut _Unwind_Context, + regclass: _Unwind_VRS_RegClass, + regno: _Unwind_Word, + repr: _Unwind_VRS_DataRepresentation, + data: *mut c_void) + -> _Unwind_VRS_Result; + } + + // On Android or ARM/Linux, these are implemented as macros: + + pub unsafe fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word { + let mut val: _Unwind_Word = 0; + _Unwind_VRS_Get(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32, + &mut val as *mut _ as *mut c_void); + val + } + + pub unsafe fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word) { + let mut value = value; + _Unwind_VRS_Set(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32, + &mut value as *mut _ as *mut c_void); + } + + pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) + -> _Unwind_Word { + let val = _Unwind_GetGR(ctx, UNWIND_IP_REG); + (val & !1) as _Unwind_Word + } + + pub unsafe fn _Unwind_SetIP(ctx: *mut _Unwind_Context, + value: _Unwind_Word) { + // Propagate thumb bit to instruction pointer + let thumb_state = _Unwind_GetGR(ctx, UNWIND_IP_REG) & 1; + let value = value | thumb_state; + _Unwind_SetGR(ctx, UNWIND_IP_REG, value); + } + + pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, + ip_before_insn: *mut c_int) + -> _Unwind_Word { + *ip_before_insn = 0; + _Unwind_GetIP(ctx) + } + + // This function also doesn't exist on Android or ARM/Linux, so make it a no-op + pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void { + pc + } +} +} // cfg_if! + +cfg_if::cfg_if! { +if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] { + // Not 32-bit iOS + #[cfg_attr(all(feature = "llvm-libunwind", + any(target_os = "fuchsia", target_os = "linux")), + link(name = "unwind", kind = "static"))] + extern "C" { + #[unwind(allowed)] + pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code; + pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, + trace_argument: *mut c_void) + -> _Unwind_Reason_Code; + } +} else { + // 32-bit iOS uses SjLj and does not provide _Unwind_Backtrace() + #[cfg_attr(all(feature = "llvm-libunwind", + any(target_os = "fuchsia", target_os = "linux")), + link(name = "unwind", kind = "static"))] + extern "C" { + #[unwind(allowed)] + pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code; + } + + #[inline] + pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) -> _Unwind_Reason_Code { + _Unwind_SjLj_RaiseException(exc) + } +} +} // cfg_if! + +cfg_if::cfg_if! { +if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] { + // We declare these as opaque types. This is fine since you just need to + // pass them to _GCC_specific_handler and forget about them. + pub enum EXCEPTION_RECORD {} + pub type LPVOID = *mut c_void; + pub enum CONTEXT {} + pub enum DISPATCHER_CONTEXT {} + pub type EXCEPTION_DISPOSITION = c_int; + type PersonalityFn = unsafe extern "C" fn(version: c_int, + actions: _Unwind_Action, + exception_class: _Unwind_Exception_Class, + exception_object: *mut _Unwind_Exception, + context: *mut _Unwind_Context) + -> _Unwind_Reason_Code; + + extern "C" { + pub fn _GCC_specific_handler(exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: LPVOID, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT, + personality: PersonalityFn) + -> EXCEPTION_DISPOSITION; + } +} +} // cfg_if! + +#[no_mangle] +extern fn abort() { + panic!("Abort!"); +} + diff --git a/src/runtime/Cargo.toml b/src/runtime/Cargo.toml index e6c662b..b656f3a 100644 --- a/src/runtime/Cargo.toml +++ b/src/runtime/Cargo.toml @@ -20,8 +20,11 @@ void = { version = "1", default-features = false } futures = { version = "0.3", default-features = false, features = ["async-await"] } async-recursion = "0.3" libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zc706.git" } -libsupport_zynq = { git = "https://git.m-labs.hk/M-Labs/zc706.git" } +libsupport_zynq = { default-features = false, git = "https://git.m-labs.hk/M-Labs/zc706.git" } libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zc706.git" } libasync = { git = "https://git.m-labs.hk/M-Labs/zc706.git" } dyld = { path = "../libdyld" } +dwarf = { path = "../libdwarf" } +unwind = { path = "../libunwind" } +libc = { path = "../libc" } fatfs = { version = "0.3", features = ["core_io"], default-features = false } diff --git a/src/runtime/link.x b/src/runtime/link.x index 4f551e1..cb1a9f7 100644 --- a/src/runtime/link.x +++ b/src/runtime/link.x @@ -17,12 +17,26 @@ MEMORY SECTIONS { + __text_start = .; .text : { KEEP(*(.text.exceptions)); *(.text.boot); *(.text .text.*); } > SDRAM + __text_end = .; + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > SDRAM + __exidx_end = .; + + .ARM.extab : + { + * (.ARM.extab*) + } > SDRAM .rodata : ALIGN(4) { @@ -62,12 +76,4 @@ SECTIONS . += 0x10000; __stack0_start = .; } > SDRAM - - /DISCARD/ : - { - /* Unused exception related info that only wastes space */ - *(.ARM.exidx); - *(.ARM.exidx.*); - *(.ARM.extab.*); - } } diff --git a/src/runtime/src/eh_artiq.rs b/src/runtime/src/eh_artiq.rs new file mode 100644 index 0000000..5122319 --- /dev/null +++ b/src/runtime/src/eh_artiq.rs @@ -0,0 +1,209 @@ +// Portions of the code in this file are derived from code by RUST developers. +// Modified to suit the case of artiq-zynq port, for ARM EHABI. +#![allow(non_camel_case_types)] + +use core::mem; +use cslice::CSlice; +use unwind as uw; +use libc::{c_int, uintptr_t}; +use log::debug; + +use dwarf::eh::{self, EHAction, EHContext}; + + +const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */ + +#[cfg(target_arch = "arm")] +const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct Exception<'a> { + pub name: CSlice<'a, u8>, + pub file: CSlice<'a, u8>, + pub line: u32, + pub column: u32, + pub function: CSlice<'a, u8>, + pub message: CSlice<'a, u8>, + pub param: [i64; 3] +} + +const MAX_BACKTRACE_SIZE: usize = 128; + +#[repr(C)] +struct ExceptionInfo { + uw_exception: uw::_Unwind_Exception, + exception: Option>, + handled: bool, + backtrace: [usize; MAX_BACKTRACE_SIZE], + backtrace_size: usize +} + + +unsafe fn find_eh_action( + context: *mut uw::_Unwind_Context, + foreign_exception: bool, +) -> Result { + let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; + let mut ip_before_instr: c_int = 0; + let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); + let eh_context = EHContext { + // The return address points 1 byte past the call instruction, + // which could be in the next IP range in LSDA range table. + ip: if ip_before_instr != 0 { ip } else { ip - 1 }, + func_start: uw::_Unwind_GetRegionStart(context), + get_text_start: &|| uw::_Unwind_GetTextRelBase(context), + get_data_start: &|| uw::_Unwind_GetDataRelBase(context), + }; + eh::find_eh_action(lsda, &eh_context, foreign_exception) +} + +pub unsafe fn artiq_personality(state: uw::_Unwind_State, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + let state = state as c_int; + let action = state & uw::_US_ACTION_MASK as c_int; + let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { + // Backtraces on ARM will call the personality routine with + // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases + // we want to continue unwinding the stack, otherwise all our backtraces + // would end at __rust_try + if state & uw::_US_FORCE_UNWIND as c_int != 0 { + return continue_unwind(exception_object, context); + } + true + } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { + false + } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { + return continue_unwind(exception_object, context); + } else { + return uw::_URC_FAILURE; + }; + + // The DWARF unwinder assumes that _Unwind_Context holds things like the function + // and LSDA pointers, however ARM EHABI places them into the exception object. + // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which + // take only the context pointer, GCC personality routines stash a pointer to + // exception_object in the context, using location reserved for ARM's + // "scratch register" (r12). + uw::_Unwind_SetGR(context, + uw::UNWIND_POINTER_REG, + exception_object as uw::_Unwind_Ptr); + // ...A more principled approach would be to provide the full definition of ARM's + // _Unwind_Context in our libunwind bindings and fetch the required data from there + // directly, bypassing DWARF compatibility functions. + + let exception_class = (*exception_object).exception_class; + let foreign_exception = exception_class != EXCEPTION_CLASS; + let eh_action = match find_eh_action(context, foreign_exception) { + Ok(action) => action, + Err(_) => return uw::_URC_FAILURE, + }; + let exception_info = &mut *(exception_object as *mut ExceptionInfo); + let exception = &exception_info.exception.unwrap(); + if search_phase { + match eh_action { + EHAction::None | + EHAction::Cleanup(_) => return continue_unwind(exception_object, context), + EHAction::Catch(_) => { + // EHABI requires the personality routine to update the + // SP value in the barrier cache of the exception object. + (*exception_object).private[5] = + uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG); + return uw::_URC_HANDLER_FOUND; + } + EHAction::Terminate => return uw::_URC_FAILURE, + } + } else { + match eh_action { + EHAction::None => return continue_unwind(exception_object, context), + EHAction::Cleanup(lpad) | + EHAction::Catch(lpad) => { + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, + exception_object as uintptr_t); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, exception as *const _ as uw::_Unwind_Word); + uw::_Unwind_SetIP(context, lpad); + return uw::_URC_INSTALL_CONTEXT; + } + EHAction::Terminate => return uw::_URC_FAILURE, + } + } + + // On ARM EHABI the personality routine is responsible for actually + // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). + unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { + uw::_URC_CONTINUE_UNWIND + } else { + uw::_URC_FAILURE + } + } + // defined in libgcc + extern "C" { + fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } +} + +extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code, + uw_exception: *mut uw::_Unwind_Exception) { + unsafe { + let exception_info = &mut *(uw_exception as *mut ExceptionInfo); + + exception_info.exception = None; + } +} + + +static mut INFLIGHT: ExceptionInfo = ExceptionInfo { + uw_exception: uw::_Unwind_Exception { + exception_class: EXCEPTION_CLASS, + exception_cleanup: cleanup, + private: [0; uw::unwinder_private_data_size], + }, + exception: None, + handled: true, + backtrace: [0; MAX_BACKTRACE_SIZE], + backtrace_size: 0 +}; + +pub unsafe extern fn raise(exception: *const Exception) -> ! { + + // Zing! The Exception<'a> to Exception<'static> transmute is not really sound in case + // the exception is ever captured. Fortunately, they currently aren't, and we save + // on the hassle of having to allocate exceptions somewhere except on stack. + debug!("Trying to raise exception"); + INFLIGHT.exception = Some(mem::transmute::>(*exception)); + INFLIGHT.handled = false; + + let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception); + assert!(result == uw::_URC_END_OF_STACK); + debug!("Result: {:?}", result); + + INFLIGHT.backtrace_size = 0; + unimplemented!("top level exception handling"); +} + +pub unsafe extern fn reraise() -> ! { + use cslice::AsCSlice; + + debug!("Re-raise"); + match INFLIGHT.exception { + Some(ref exception) => raise(exception), + None => raise(&Exception { + name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(), + file: file!().as_c_slice(), + line: line!(), + column: column!(), + // https://github.com/rust-lang/rfcs/pull/1719 + function: "__artiq_reraise".as_c_slice(), + message: "No active exception to reraise".as_c_slice(), + param: [0, 0, 0] + }) + } +} + diff --git a/src/runtime/src/kernel.rs b/src/runtime/src/kernel.rs index a590b1e..3b63fb1 100644 --- a/src/runtime/src/kernel.rs +++ b/src/runtime/src/kernel.rs @@ -7,6 +7,8 @@ use libcortex_a9::{cache::dcci_slice, mutex::Mutex, sync_channel::{self, sync_ch use libsupport_zynq::boot::Core1; use dyld; +use unwind; +use crate::eh_artiq; use crate::rpc; use crate::rtio; @@ -70,7 +72,7 @@ fn rpc_send_common(is_async: bool, service: u32, tag: &CSlice, data: *const let core1_tx: &mut sync_channel::Sender = unsafe { mem::transmute(KERNEL_CHANNEL_1TO0) }; let mut buffer = Vec::::new(); rpc::send_args(&mut buffer, service, tag.as_ref(), data).expect("RPC encoding failed"); - core1_tx.send(Message::RpcSend { is_async: is_async, data: Arc::new(buffer) }); + core1_tx.send(Message::RpcSend { is_async, data: Arc::new(buffer) }); } extern fn rpc_send(service: u32, tag: &CSlice, data: *const *const ()) { @@ -132,10 +134,6 @@ extern fn rpc_recv(slot: *mut ()) -> usize { } } -extern fn exception_unimplemented() { - unimplemented!(); -} - macro_rules! api { ($i:ident) => ({ extern { static $i: u8; } @@ -250,12 +248,13 @@ fn resolve(required: &[u8]) -> Option { api!(__aeabi_memclr8), api!(__aeabi_memclr4), api!(__aeabi_memclr), - + // libc + api!(memcmp, extern { fn memcmp(a: *const u8, b: *mut u8, size: usize); }), // exceptions - api!(_Unwind_Resume = exception_unimplemented), - api!(__artiq_personality = exception_unimplemented), - api!(__artiq_raise = exception_unimplemented), - api!(__artiq_reraise = exception_unimplemented), + api!(_Unwind_Resume = unwind::_Unwind_Resume), + api!(__artiq_personality = eh_artiq::artiq_personality), + api!(__artiq_raise = eh_artiq::raise), + api!(__artiq_reraise = eh_artiq::reraise), ]; api.iter() diff --git a/src/runtime/src/main.rs b/src/runtime/src/main.rs index 4734b47..74a7a26 100644 --- a/src/runtime/src/main.rs +++ b/src/runtime/src/main.rs @@ -2,13 +2,15 @@ #![no_main] #![recursion_limit="1024"] // for futures_util::select! #![feature(llvm_asm)] +#![feature(unwind_attributes)] +#![feature(panic_info_message)] extern crate alloc; use core::{cmp, str}; use log::{info, error}; -use libboard_zynq::{timer::GlobalTimer, logger, devc}; +use libboard_zynq::{timer::GlobalTimer, logger, devc, stdio}; use libsupport_zynq::ram; mod sd_reader; @@ -23,6 +25,8 @@ mod rtio; mod kernel; mod moninj; mod load_pl; +mod eh_artiq; +mod panic; fn identifier_read(buf: &mut [u8]) -> &str { unsafe { @@ -37,6 +41,12 @@ fn identifier_read(buf: &mut [u8]) -> &str { } } +#[no_mangle] +extern "C" fn _putchar(byte: u8) { + let mut uart = stdio::get_uart(); + uart.write_byte(byte); +} + #[no_mangle] pub fn main_core0() { let timer = GlobalTimer::start(); @@ -70,6 +80,7 @@ pub fn main_core0() { // what we want (e.g. gateware configured via JTAG before PS // startup, or by FSBL). } else { + // panic!("gateware not loaded"); // Load from SD card match load_pl::load_bitstream_from_sd() { Ok(_) => info!("Bitstream loaded successfully!"), @@ -82,5 +93,7 @@ pub fn main_core0() { pl::csr::rtio_core::reset_phy_write(1); } + info!("Starting communication"); + comms::main(timer); } diff --git a/src/runtime/src/panic.rs b/src/runtime/src/panic.rs new file mode 100644 index 0000000..1011f9e --- /dev/null +++ b/src/runtime/src/panic.rs @@ -0,0 +1,38 @@ +use unwind::backtrace; +use libboard_zynq::{slcr, print, println}; + +static mut PANICKED: bool = false; + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + print!("panic at "); + if let Some(location) = info.location() { + print!("{}:{}:{}", location.file(), location.line(), location.column()); + } else { + print!("unknown location"); + } + if let Some(message) = info.message() { + println!(": {}", message); + } else { + println!(""); + } + + if unsafe { PANICKED } { + print!("Panic in panic!"); + } else { + unsafe { + PANICKED = true; + } + + // read backtrace can trigger panic. + println!("Backtrace:"); + let _ = backtrace(|ip| { + // Backtrace gives us the return address, i.e. the address after the delay slot, + // but we're interested in the call instruction. + println!("{:#08x}", ip - 2 * 4); + }); + } + + slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset()); + loop {} +}