firmware: migrate to Rust 1.28.0.

This also updates / is a prerequisite for updating smoltcp.

Rationale for changes made:
  * compiler_builtins is now shipped in the rust prefix.
  * rustc's libpanic_unwind no longer works for us because it
    has a hard dependency on Box (and it's a horrible hack);
    fortunately, we only ever needed a personality function
    from it.
  * panic and oom handlers are now set in a completely different
    way.
  * allocators are quite different (and finally stable).
  * NLL caused internal compiler errors in runtime, so code using
    NLL was rewritten to not rely on it and it was turned off.
This commit is contained in:
whitequark 2018-08-10 15:24:51 +00:00
parent 738d2c6bcb
commit 2648b1b7a1
25 changed files with 721 additions and 581 deletions

View File

@ -30,9 +30,8 @@ dependencies = [
"build_misoc 0.0.0",
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
"compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=181083f)",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=2139686)",
]
[[package]]
@ -43,7 +42,7 @@ dependencies = [
"build_misoc 0.0.0",
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=181083f)",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=2139686)",
]
[[package]]
@ -70,11 +69,6 @@ name = "cfg-if"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "compiler_builtins"
version = "0.1.0"
source = "git+https://github.com/rust-lang-nursery/compiler-builtins#28daccd9159d33fac5e36394e4f9618db8870dc0"
[[package]]
name = "crc"
version = "1.8.1"
@ -92,6 +86,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "dyld"
version = "0.0.0"
[[package]]
name = "eh"
version = "0.0.0"
dependencies = [
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure"
version = "0.1.1"
@ -110,7 +111,7 @@ dependencies = [
[[package]]
name = "fringe"
version = "1.1.0"
source = "git+https://github.com/m-labs/libfringe?rev=bd23494#bd2349467157969324ca7da5d2ae033c7ffac0c0"
source = "git+https://github.com/m-labs/libfringe?rev=b8a6d8f#b8a6d8f68df0edaa3d67d9f3b7b62af9d3bb64a5"
dependencies = [
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -133,6 +134,7 @@ dependencies = [
"build_misoc 0.0.0",
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dyld 0.0.0",
"eh 0.0.0",
"io 0.0.0",
"proto_artiq 0.0.0",
]
@ -174,12 +176,7 @@ dependencies = [
[[package]]
name = "managed"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "managed"
version = "0.6.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -210,15 +207,16 @@ dependencies = [
"build_misoc 0.0.0",
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"eh 0.0.0",
"failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=bd23494)",
"fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=b8a6d8f)",
"io 0.0.0",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"logger_artiq 0.0.0",
"managed 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proto_artiq 0.0.0",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=181083f)",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=2139686)",
"unwind_backtrace 0.0.0",
]
@ -235,11 +233,12 @@ dependencies = [
[[package]]
name = "smoltcp"
version = "0.4.0"
source = "git+https://github.com/m-labs/smoltcp?rev=181083f#181083f18c977b8a0463a67e360e4db20594fa21"
source = "git+https://github.com/m-labs/smoltcp?rev=2139686#21396867114d267da06f19cc54cc4a1883b900a5"
dependencies = [
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"managed 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -284,20 +283,18 @@ version = "0.0.0"
"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9"
"checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba"
"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18"
"checksum compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins)" = "<none>"
"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
"checksum cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a"
"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82"
"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b"
"checksum fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=bd23494)" = "<none>"
"checksum fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=b8a6d8f)" = "<none>"
"checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum log_buffer 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f033173c9486b7fe97a79c895c0a3483ae395ab6744c985d10078950e2492419"
"checksum managed 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43e2737ecabe4ae36a68061398bf27d2bfd0763f4c3c837a398478459494c4b7"
"checksum managed 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5a31885241e61ba264d780d2e6686e7e59561c947b4581470364eb3e10102d86"
"checksum managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba6713e624266d7600e9feae51b1926c6a6a6bebb18ec5a8e11a5f1d5661baba"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=181083f)" = "<none>"
"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=2139686)" = "<none>"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd"

View File

@ -19,6 +19,6 @@ board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp
[dependencies.smoltcp]
git = "https://github.com/m-labs/smoltcp"
rev = "181083f"
rev = "2139686"
default-features = false
features = ["proto-ipv4", "socket-tcp"]

View File

@ -15,6 +15,13 @@ SECTIONS
*(.text .text.*)
} > rom
/*
* The compiler_builtins crate includes some GOTPC relocations, which require a GOT symbol,
* but don't actually need a GOT. This really ought to be fixed on rustc level, but I'm afraid
* it will add further complications to our build system that aren't pulling their weight.
*/
_GLOBAL_OFFSET_TABLE_ = .;
.rodata :
{
*(.rodata.*)

View File

@ -1,5 +1,5 @@
#![no_std]
#![feature(lang_items)]
#![feature(panic_implementation, panic_info_message)]
extern crate crc;
extern crate byteorder;
@ -188,7 +188,8 @@ fn network_boot() {
println!("Waiting for connections...");
loop {
match interface.poll(&mut sockets, clock::get_ms()) {
let timestamp = smoltcp::time::Instant::from_millis(clock::get_ms() as i64);
match interface.poll(&mut sockets, timestamp) {
Ok(_) => (),
Err(smoltcp::Error::Unrecognized) => (),
Err(err) => println!("Network error: {}", err)
@ -232,10 +233,18 @@ pub extern fn abort() {
loop {}
}
#[no_mangle]
#[lang = "panic_fmt"]
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str,
line: u32, column: u32) -> ! {
println!("panic at {}:{}:{}: {}", file, line, column, args);
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_implementation]
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
if let Some(location) = info.location() {
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("panic at unknown location");
}
if let Some(message) = info.message() {
println!(": {}", message);
} else {
println!("");
}
loop {}
}

View File

@ -14,6 +14,7 @@ build_misoc = { path = "../libbuild_misoc" }
[dependencies]
cslice = { version = "0.3" }
eh = { path = "../libeh" }
io = { path = "../libio", features = ["byteorder"] }
dyld = { path = "../libdyld" }
board_misoc = { path = "../libboard_misoc" }

View File

@ -71,9 +71,9 @@ static mut API: &'static [(&'static str, *const ())] = &[
/* exceptions */
api!(_Unwind_Resume = ::unwind::_Unwind_Resume),
api!(__artiq_personality = ::eh::personality),
api!(__artiq_raise = ::eh::raise),
api!(__artiq_reraise = ::eh::reraise),
api!(__artiq_personality = ::eh_artiq::personality),
api!(__artiq_raise = ::eh_artiq::raise),
api!(__artiq_reraise = ::eh_artiq::reraise),
/* proxified syscalls */
api!(core_log),

View File

@ -1,465 +0,0 @@
// Portions of the code in this file are derived from code by:
//
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(non_upper_case_globals, non_camel_case_types, dead_code)]
use core::{ptr, mem};
use cslice::CSlice;
use unwind as uw;
use libc::{c_int, c_void};
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code;
extern {
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
stop_fn: _Unwind_Stop_Fn,
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
}
const DW_EH_PE_omit: u8 = 0xFF;
const DW_EH_PE_absptr: u8 = 0x00;
const DW_EH_PE_uleb128: u8 = 0x01;
const DW_EH_PE_udata2: u8 = 0x02;
const DW_EH_PE_udata4: u8 = 0x03;
const DW_EH_PE_udata8: u8 = 0x04;
const DW_EH_PE_sleb128: u8 = 0x09;
const DW_EH_PE_sdata2: u8 = 0x0A;
const DW_EH_PE_sdata4: u8 = 0x0B;
const DW_EH_PE_sdata8: u8 = 0x0C;
const DW_EH_PE_pcrel: u8 = 0x10;
const DW_EH_PE_textrel: u8 = 0x20;
const DW_EH_PE_datarel: u8 = 0x30;
const DW_EH_PE_funcrel: u8 = 0x40;
const DW_EH_PE_aligned: u8 = 0x50;
const DW_EH_PE_indirect: u8 = 0x80;
#[derive(Clone)]
struct DwarfReader {
pub ptr: *const u8,
}
impl DwarfReader {
fn new(ptr: *const u8) -> DwarfReader {
DwarfReader { ptr: 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.
unsafe fn read<T: Copy>(&mut self) -> T {
let result = ptr::read_unaligned(self.ptr as *const T);
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
result
}
// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
// Length Data".
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::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
result
}
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::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
// sign-extend
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
result |= (!0 as u64) << shift;
}
result as i64
}
unsafe fn read_encoded_pointer(&mut self, encoding: u8) -> usize {
fn round_up(unrounded: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two());
(unrounded + align - 1) & !(align - 1)
}
debug_assert!(encoding != DW_EH_PE_omit);
// DW_EH_PE_aligned implies it's an absolute pointer value
if encoding == DW_EH_PE_aligned {
self.ptr = round_up(self.ptr as usize, mem::size_of::<usize>()) as *const u8;
return self.read::<usize>()
}
let value_ptr = self.ptr;
let mut result = match encoding & 0x0F {
DW_EH_PE_absptr => self.read::<usize>(),
DW_EH_PE_uleb128 => self.read_uleb128() as usize,
DW_EH_PE_udata2 => self.read::<u16>() as usize,
DW_EH_PE_udata4 => self.read::<u32>() as usize,
DW_EH_PE_udata8 => self.read::<u64>() as usize,
DW_EH_PE_sleb128 => self.read_sleb128() as usize,
DW_EH_PE_sdata2 => self.read::<i16>() as usize,
DW_EH_PE_sdata4 => self.read::<i32>() as usize,
DW_EH_PE_sdata8 => self.read::<i64>() as usize,
_ => panic!(),
};
result += match encoding & 0x70 {
DW_EH_PE_absptr => 0,
// relative to address of the encoded value, despite the name
DW_EH_PE_pcrel => value_ptr as usize,
_ => panic!(),
};
if encoding & DW_EH_PE_indirect != 0 {
result = *(result as *const usize);
}
result
}
}
fn encoding_size(encoding: u8) -> usize {
if encoding == DW_EH_PE_omit {
return 0
}
match encoding & 0x0F {
DW_EH_PE_absptr => mem::size_of::<usize>(),
DW_EH_PE_udata2 => 2,
DW_EH_PE_udata4 => 4,
DW_EH_PE_udata8 => 8,
DW_EH_PE_sdata2 => 2,
DW_EH_PE_sdata4 => 4,
DW_EH_PE_sdata8 => 8,
_ => panic!()
}
}
pub enum EHAction {
None,
Cleanup(usize),
Catch(usize),
Terminate,
}
unsafe fn find_eh_action(lsda: *const u8, func_start: usize, ip: usize,
exn_name: CSlice<u8>) -> EHAction {
if lsda.is_null() {
return EHAction::None
}
let mut reader = DwarfReader::new(lsda);
let start_encoding = reader.read::<u8>();
// base address for landing pad offsets
let lpad_base = if start_encoding != DW_EH_PE_omit {
reader.read_encoded_pointer(start_encoding)
} else {
func_start
};
let ttype_encoding = reader.read::<u8>();
let ttype_encoding_size = encoding_size(ttype_encoding) as isize;
let class_info;
if ttype_encoding != DW_EH_PE_omit {
let class_info_offset = reader.read_uleb128();
class_info = reader.ptr.offset(class_info_offset as isize);
} else {
class_info = ptr::null();
}
assert!(!class_info.is_null());
let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128();
let action_table = reader.ptr.offset(call_site_table_length as isize);
while reader.ptr < action_table {
let cs_start = reader.read_encoded_pointer(call_site_encoding);
let cs_len = reader.read_encoded_pointer(call_site_encoding);
let cs_lpad = reader.read_encoded_pointer(call_site_encoding);
let cs_action = reader.read_uleb128();
if ip < func_start + cs_start {
// Callsite table is sorted by cs_start, so if we've passed the ip, we
// may stop searching.
break
}
if ip > func_start + cs_start + cs_len {
continue
}
if cs_lpad == 0 {
return EHAction::None
}
let lpad = lpad_base + cs_lpad;
if cs_action == 0 {
return EHAction::Cleanup(lpad)
}
let action_entry = action_table.offset((cs_action - 1) as isize);
let mut action_reader = DwarfReader::new(action_entry);
loop {
let type_info_offset = action_reader.read_sleb128() as isize;
let action_offset = action_reader.clone().read_sleb128() as isize;
assert!(type_info_offset >= 0);
if type_info_offset > 0 {
let type_info_ptr_ptr = class_info.offset(-type_info_offset * ttype_encoding_size);
let type_info_ptr = DwarfReader::new(type_info_ptr_ptr)
.read_encoded_pointer(ttype_encoding);
let type_info = *(type_info_ptr as *const CSlice<u8>);
if type_info.as_ref() == exn_name.as_ref() {
return EHAction::Catch(lpad)
}
if type_info.len() == 0 {
// This is a catch-all clause. We don't compare type_info_ptr with null here
// because, in PIC mode, the OR1K LLVM backend emits a literal zero
// encoded with DW_EH_PE_pcrel, which of course doesn't result in
// a proper null pointer.
return EHAction::Catch(lpad)
}
}
if action_offset == 0 {
break
} else {
action_reader.ptr = action_reader.ptr.offset(action_offset)
}
}
return EHAction::None
}
// the function has a personality but no landing pads; this is fine
EHAction::None
}
#[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 EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */
const MAX_BACKTRACE_SIZE: usize = 128;
#[repr(C)]
struct ExceptionInfo {
uw_exception: uw::_Unwind_Exception,
exception: Option<Exception<'static>>,
handled: bool,
backtrace: [usize; MAX_BACKTRACE_SIZE],
backtrace_size: usize
}
#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
#[cfg(any(target_arch = "or1k"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
#[export_name="__artiq_personality"]
pub extern fn personality(version: c_int,
actions: uw::_Unwind_Action,
uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
unsafe {
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
return uw::_URC_FATAL_PHASE1_ERROR
}
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let ip = uw::_Unwind_GetIP(context) - 1;
let func_start = uw::_Unwind_GetRegionStart(context);
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
let exception = &exception_info.exception.unwrap();
let eh_action = find_eh_action(lsda, func_start, ip, exception.name);
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
if actions as u32 & uw::_UA_HANDLER_FRAME as u32 != 0 {
exception_info.handled = true
}
// Pass a pair of the unwinder exception and ARTIQ exception
// (which immediately follows).
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
uw_exception as uw::_Unwind_Word);
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_FATAL_PHASE2_ERROR,
}
}
}
}
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;
}
}
extern fn uncaught_exception(_version: c_int,
actions: uw::_Unwind_Action,
_uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
_stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
if exception_info.backtrace_size < exception_info.backtrace.len() {
let ip = uw::_Unwind_GetIP(context);
exception_info.backtrace[exception_info.backtrace_size] = ip;
exception_info.backtrace_size += 1;
}
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
::terminate(&exception_info.exception.unwrap(),
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
} else {
uw::_URC_NO_REASON
}
}
}
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
// See https://github.com/rust-lang/rust/issues/39498.
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
};
#[export_name="__artiq_raise"]
#[unwind(allowed)]
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.
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
INFLIGHT.handled = false;
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
assert!(result == uw::_URC_END_OF_STACK);
INFLIGHT.backtrace_size = 0;
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
uncaught_exception, ptr::null_mut());
unreachable!()
}
#[export_name="__artiq_reraise"]
#[unwind(allowed)]
pub unsafe extern fn reraise() -> ! {
use cslice::AsCSlice;
if INFLIGHT.handled {
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]
})
}
} else {
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
}
}
// Stub implementations for the functions the panic_unwind crate expects to be provided.
// These all do nothing in libunwind, but aren't built for OR1K.
pub mod stubs {
#![allow(bad_style, unused_variables)]
use super::{uw, c_int};
#[export_name="_Unwind_GetIPInfo"]
pub unsafe extern fn _Unwind_GetIPInfo(ctx: *mut uw::_Unwind_Context,
ip_before_insn: *mut c_int) -> uw::_Unwind_Word {
*ip_before_insn = 0;
uw::_Unwind_GetIP(ctx)
}
#[export_name="_Unwind_GetTextRelBase"]
pub unsafe extern fn _Unwind_GetTextRelBase(ctx: *mut uw::_Unwind_Context) -> uw::_Unwind_Ptr {
unimplemented!()
}
#[export_name="_Unwind_GetDataRelBase"]
pub unsafe extern fn _Unwind_GetDataRelBase(ctx: *mut uw::_Unwind_Context) -> uw::_Unwind_Ptr {
unimplemented!()
}
}

View File

@ -0,0 +1,203 @@
// Portions of the code in this file are derived from code by:
//
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(private_no_mangle_fns, non_camel_case_types)]
use core::{ptr, mem};
use cslice::CSlice;
use unwind as uw;
use libc::{c_int, c_void};
use eh::dwarf::{self, EHAction};
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code;
extern {
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
stop_fn: _Unwind_Stop_Fn,
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
}
#[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 EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */
const MAX_BACKTRACE_SIZE: usize = 128;
#[repr(C)]
struct ExceptionInfo {
uw_exception: uw::_Unwind_Exception,
exception: Option<Exception<'static>>,
handled: bool,
backtrace: [usize; MAX_BACKTRACE_SIZE],
backtrace_size: usize
}
#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
#[cfg(any(target_arch = "or1k"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
#[export_name="__artiq_personality"]
pub extern fn personality(version: c_int,
actions: uw::_Unwind_Action,
uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
unsafe {
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
return uw::_URC_FATAL_PHASE1_ERROR
}
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let ip = uw::_Unwind_GetIP(context) - 1;
let func_start = uw::_Unwind_GetRegionStart(context);
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
let exception = &exception_info.exception.unwrap();
let eh_action = dwarf::find_eh_action(lsda, func_start, ip, exception.name);
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
if actions as u32 & uw::_UA_HANDLER_FRAME as u32 != 0 {
exception_info.handled = true
}
// Pass a pair of the unwinder exception and ARTIQ exception
// (which immediately follows).
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
uw_exception as uw::_Unwind_Word);
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_FATAL_PHASE2_ERROR,
}
}
}
}
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;
}
}
extern fn uncaught_exception(_version: c_int,
actions: uw::_Unwind_Action,
_uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
_stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
if exception_info.backtrace_size < exception_info.backtrace.len() {
let ip = uw::_Unwind_GetIP(context);
exception_info.backtrace[exception_info.backtrace_size] = ip;
exception_info.backtrace_size += 1;
}
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
::terminate(&exception_info.exception.unwrap(),
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
} else {
uw::_URC_NO_REASON
}
}
}
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
// See https://github.com/rust-lang/rust/issues/39498.
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
};
#[export_name="__artiq_raise"]
#[unwind(allowed)]
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.
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
INFLIGHT.handled = false;
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
assert!(result == uw::_URC_END_OF_STACK);
INFLIGHT.backtrace_size = 0;
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
uncaught_exception, ptr::null_mut());
unreachable!()
}
#[export_name="__artiq_reraise"]
#[unwind(allowed)]
pub unsafe extern fn reraise() -> ! {
use cslice::AsCSlice;
if INFLIGHT.handled {
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]
})
}
} else {
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
}
}

View File

@ -1,12 +1,12 @@
#![feature(lang_items, asm, libc, panic_unwind, unwind_attributes, global_allocator,
needs_panic_runtime)]
#![feature(lang_items, asm, panic_unwind, libc, unwind_attributes,
panic_implementation, panic_info_message)]
#![no_std]
#![needs_panic_runtime]
extern crate cslice;
extern crate unwind;
extern crate libc;
extern crate unwind;
extern crate cslice;
extern crate eh;
extern crate io;
extern crate dyld;
extern crate board_misoc;
@ -46,11 +46,20 @@ macro_rules! recv {
}
}
#[no_mangle]
#[lang = "panic_fmt"]
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str,
line: u32, column: u32) -> ! {
send(&Log(format_args!("panic at {}:{}:{}: {}\n", file, line, column, args)));
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_implementation]
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
if let Some(location) = info.location() {
send(&Log(format_args!("panic at {}:{}:{}",
location.file(), location.line(), location.column())));
} else {
send(&Log(format_args!("panic at unknown location")));
}
if let Some(message) = info.message() {
send(&Log(format_args!("{}\n", message)));
} else {
send(&Log(format_args!("\n")));
}
send(&RunAborted);
loop {}
}
@ -67,7 +76,7 @@ macro_rules! println {
macro_rules! raise {
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
use cslice::AsCSlice;
let exn = $crate::eh::Exception {
let exn = $crate::eh_artiq::Exception {
name: concat!("0:artiq.coredevice.exceptions.", $name).as_c_slice(),
file: file!().as_c_slice(),
line: line!(),
@ -78,14 +87,14 @@ macro_rules! raise {
param: [$param0, $param1, $param2]
};
#[allow(unused_unsafe)]
unsafe { $crate::eh::raise(&exn) }
unsafe { $crate::eh_artiq::raise(&exn) }
});
($name:expr, $message:expr) => ({
raise!($name, $message, 0, 0, 0)
});
}
pub mod eh;
mod eh_artiq;
mod api;
mod rtio;
mod nrt_bus;
@ -109,6 +118,7 @@ pub extern fn send_to_rtio_log(timestamp: i64, text: CSlice<u8>) {
rtio::log(timestamp, text.as_ref())
}
#[unwind(aborts)]
extern fn rpc_send(service: u32, tag: CSlice<u8>, data: *const *const ()) {
while !rpc_queue::empty() {}
send(&RpcSend {
@ -119,6 +129,7 @@ extern fn rpc_send(service: u32, tag: CSlice<u8>, data: *const *const ()) {
})
}
#[unwind(aborts)]
extern fn rpc_send_async(service: u32, tag: CSlice<u8>, data: *const *const ()) {
while rpc_queue::full() {}
rpc_queue::enqueue(|mut slice| {
@ -141,6 +152,7 @@ extern fn rpc_send_async(service: u32, tag: CSlice<u8>, data: *const *const ())
})
}
#[unwind(allowed)]
extern fn rpc_recv(slot: *mut ()) -> usize {
send(&RpcRecvRequest(slot));
recv!(&RpcRecvReply(ref result) => {
@ -148,7 +160,7 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
&Ok(alloc_size) => alloc_size,
&Err(ref exception) =>
unsafe {
eh::raise(&eh::Exception {
eh_artiq::raise(&eh_artiq::Exception {
name: exception.name.as_bytes().as_c_slice(),
file: exception.file.as_bytes().as_c_slice(),
line: exception.line,
@ -162,7 +174,7 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
})
}
fn terminate(exception: &eh::Exception, backtrace: &mut [usize]) -> ! {
fn terminate(exception: &eh_artiq::Exception, backtrace: &mut [usize]) -> ! {
let mut cursor = 0;
for index in 0..backtrace.len() {
if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS {
@ -188,6 +200,7 @@ fn terminate(exception: &eh::Exception, backtrace: &mut [usize]) -> ! {
loop {}
}
#[unwind(allowed)]
extern fn watchdog_set(ms: i64) -> i32 {
if ms < 0 {
raise!("ValueError", "cannot set a watchdog with a negative timeout")
@ -197,10 +210,12 @@ extern fn watchdog_set(ms: i64) -> i32 {
recv!(&WatchdogSetReply { id } => id) as i32
}
#[unwind(aborts)]
extern fn watchdog_clear(id: i32) {
send(&WatchdogClear { id: id as usize })
}
#[unwind(aborts)]
extern fn cache_get(key: CSlice<u8>) -> CSlice<'static, i32> {
send(&CacheGetRequest {
key: str::from_utf8(key.as_ref()).unwrap()
@ -208,6 +223,7 @@ extern fn cache_get(key: CSlice<u8>) -> CSlice<'static, i32> {
recv!(&CacheGetReply { value } => value.as_c_slice())
}
#[unwind(allowed)]
extern fn cache_put(key: CSlice<u8>, list: CSlice<i32>) {
send(&CachePutRequest {
key: str::from_utf8(key.as_ref()).unwrap(),
@ -241,6 +257,7 @@ fn dma_record_flush() {
}
}
#[unwind(allowed)]
extern fn dma_record_start(name: CSlice<u8>) {
let name = str::from_utf8(name.as_ref()).unwrap();
@ -260,6 +277,7 @@ extern fn dma_record_start(name: CSlice<u8>) {
}
}
#[unwind(allowed)]
extern fn dma_record_stop(duration: i64) {
unsafe {
dma_record_flush();
@ -281,10 +299,12 @@ extern fn dma_record_stop(duration: i64) {
}
}
#[unwind(aborts)]
extern fn dma_record_output(timestamp: i64, channel: i32, address: i32, word: i32) {
dma_record_output_wide(timestamp, channel, address, [word].as_c_slice())
}
#[unwind(aborts)]
extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, words: CSlice<i32>) {
assert!(words.len() <= 16); // enforce the hardware limit
@ -333,6 +353,7 @@ extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, wor
}
}
#[unwind(aborts)]
extern fn dma_erase(name: CSlice<u8>) {
let name = str::from_utf8(name.as_ref()).unwrap();
@ -345,6 +366,7 @@ struct DmaTrace {
address: i32,
}
#[unwind(allowed)]
extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
let name = str::from_utf8(name.as_ref()).unwrap();
@ -365,6 +387,7 @@ extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
}
#[cfg(has_rtio_dma)]
#[unwind(allowed)]
extern fn dma_playback(timestamp: i64, ptr: i32) {
assert!(ptr % 64 == 0);
@ -399,6 +422,7 @@ extern fn dma_playback(timestamp: i64, ptr: i32) {
}
#[cfg(not(has_rtio_dma))]
#[unwind(allowed)]
extern fn dma_playback(_timestamp: i64, _ptr: i32) {
unimplemented!("not(has_rtio_dma)")
}
@ -485,11 +509,13 @@ pub unsafe fn main() {
}
#[no_mangle]
#[unwind(allowed)]
pub extern fn exception(vect: u32, _regs: *const u32, pc: u32, ea: u32) {
panic!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea)
}
#[no_mangle]
#[unwind(allowed)]
pub extern fn abort() {
panic!("aborted")
}

View File

@ -1,10 +1,7 @@
#![feature(alloc, allocator_api)]
#![no_std]
extern crate alloc;
use core::{mem, fmt};
use alloc::allocator::{Layout, AllocErr, Alloc};
use core::{ptr, mem, fmt};
use core::alloc::{GlobalAlloc, Layout};
// The minimum alignment guaranteed by the architecture.
const MIN_ALIGN: usize = 4;
@ -42,10 +39,10 @@ impl ListAlloc {
}
}
unsafe impl<'a> Alloc for &'a ListAlloc {
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
unsafe impl GlobalAlloc for ListAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.align() > MIN_ALIGN {
return Err(AllocErr::Unsupported { details: "alignment too large" })
panic!("cannot allocate with alignment {}", layout.align())
}
let header_size = mem::size_of::<Header>();
@ -83,7 +80,7 @@ unsafe impl<'a> Alloc for &'a ListAlloc {
if (*curr).size >= size {
(*curr).magic = MAGIC_BUSY;
return Ok(curr.offset(1) as *mut u8)
return curr.offset(1) as *mut u8
}
},
_ => panic!("heap corruption detected at {:p}", curr)
@ -92,20 +89,16 @@ unsafe impl<'a> Alloc for &'a ListAlloc {
curr = (*curr).next;
}
Err(AllocErr::Exhausted { request: layout })
ptr::null_mut()
}
unsafe fn dealloc(&mut self, ptr: *mut u8, _layout: Layout) {
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
let curr = (ptr as *mut Header).offset(-1);
if (*curr).magic != MAGIC_BUSY {
panic!("heap corruption detected at {:p}", curr)
}
(*curr).magic = MAGIC_FREE;
}
fn oom(&mut self, err: AllocErr) -> ! {
panic!("heap view: {}\ncannot allocate: {:?}", self, err)
}
}
impl fmt::Display for ListAlloc {

View File

@ -13,13 +13,12 @@ cc = "1.0"
build_misoc = { path = "../libbuild_misoc" }
[dependencies]
compiler_builtins = { git = "https://github.com/rust-lang-nursery/compiler-builtins", features = ["mem"] }
byteorder = { version = "1.0", default-features = false }
log = { version = "0.4", default-features = false, optional = true }
[dependencies.smoltcp]
git = "https://github.com/m-labs/smoltcp"
rev = "181083f"
rev = "2139686" # NB: also change in runtime/Cargo.toml
default-features = false
optional = true

View File

@ -1,5 +1,6 @@
use core::{slice, fmt};
use smoltcp::Result;
use smoltcp::time::Instant;
use smoltcp::phy::{self, DeviceCapabilities, Device};
use csr;
@ -95,7 +96,7 @@ impl<'a> Device<'a> for EthernetDevice {
pub struct EthernetRxSlot(usize);
impl phy::RxToken for EthernetRxSlot {
fn consume<R, F>(self, _timestamp: u64, f: F) -> Result<R>
fn consume<R, F>(self, _timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&[u8]) -> Result<R>
{
unsafe {
@ -110,7 +111,7 @@ impl phy::RxToken for EthernetRxSlot {
pub struct EthernetTxSlot(usize);
impl phy::TxToken for EthernetTxSlot {
fn consume<R, F>(self, _timestamp: u64, length: usize, f: F) -> Result<R>
fn consume<R, F>(self, _timestamp: Instant, length: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
{
debug_assert!(length < SLOT_SIZE);

View File

@ -1,7 +1,6 @@
#![no_std]
#![feature(compiler_builtins_lib, asm, try_from)]
#![feature(asm, try_from)]
extern crate compiler_builtins;
extern crate byteorder;
#[cfg(feature = "log")]
extern crate log;

View File

@ -0,0 +1,11 @@
[package]
authors = ["M-Labs"]
name = "eh"
version = "0.0.0"
[lib]
name = "eh"
path = "lib.rs"
[dependencies]
cslice = { version = "0.3" }

View File

@ -0,0 +1,243 @@
#![allow(non_upper_case_globals, dead_code)]
use core::{ptr, mem};
use cslice::CSlice;
const DW_EH_PE_omit: u8 = 0xFF;
const DW_EH_PE_absptr: u8 = 0x00;
const DW_EH_PE_uleb128: u8 = 0x01;
const DW_EH_PE_udata2: u8 = 0x02;
const DW_EH_PE_udata4: u8 = 0x03;
const DW_EH_PE_udata8: u8 = 0x04;
const DW_EH_PE_sleb128: u8 = 0x09;
const DW_EH_PE_sdata2: u8 = 0x0A;
const DW_EH_PE_sdata4: u8 = 0x0B;
const DW_EH_PE_sdata8: u8 = 0x0C;
const DW_EH_PE_pcrel: u8 = 0x10;
const DW_EH_PE_textrel: u8 = 0x20;
const DW_EH_PE_datarel: u8 = 0x30;
const DW_EH_PE_funcrel: u8 = 0x40;
const DW_EH_PE_aligned: u8 = 0x50;
const DW_EH_PE_indirect: u8 = 0x80;
#[derive(Clone)]
struct DwarfReader {
pub ptr: *const u8,
}
impl DwarfReader {
fn new(ptr: *const u8) -> DwarfReader {
DwarfReader { ptr: 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.
unsafe fn read<T: Copy>(&mut self) -> T {
let result = ptr::read_unaligned(self.ptr as *const T);
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
result
}
// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
// Length Data".
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::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
result
}
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::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
// sign-extend
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
result |= (!0 as u64) << shift;
}
result as i64
}
unsafe fn read_encoded_pointer(&mut self, encoding: u8) -> usize {
fn round_up(unrounded: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two());
(unrounded + align - 1) & !(align - 1)
}
debug_assert!(encoding != DW_EH_PE_omit);
// DW_EH_PE_aligned implies it's an absolute pointer value
if encoding == DW_EH_PE_aligned {
self.ptr = round_up(self.ptr as usize, mem::size_of::<usize>()) as *const u8;
return self.read::<usize>()
}
let value_ptr = self.ptr;
let mut result = match encoding & 0x0F {
DW_EH_PE_absptr => self.read::<usize>(),
DW_EH_PE_uleb128 => self.read_uleb128() as usize,
DW_EH_PE_udata2 => self.read::<u16>() as usize,
DW_EH_PE_udata4 => self.read::<u32>() as usize,
DW_EH_PE_udata8 => self.read::<u64>() as usize,
DW_EH_PE_sleb128 => self.read_sleb128() as usize,
DW_EH_PE_sdata2 => self.read::<i16>() as usize,
DW_EH_PE_sdata4 => self.read::<i32>() as usize,
DW_EH_PE_sdata8 => self.read::<i64>() as usize,
_ => panic!(),
};
result += match encoding & 0x70 {
DW_EH_PE_absptr => 0,
// relative to address of the encoded value, despite the name
DW_EH_PE_pcrel => value_ptr as usize,
_ => panic!(),
};
if encoding & DW_EH_PE_indirect != 0 {
result = *(result as *const usize);
}
result
}
}
fn encoding_size(encoding: u8) -> usize {
if encoding == DW_EH_PE_omit {
return 0
}
match encoding & 0x0F {
DW_EH_PE_absptr => mem::size_of::<usize>(),
DW_EH_PE_udata2 => 2,
DW_EH_PE_udata4 => 4,
DW_EH_PE_udata8 => 8,
DW_EH_PE_sdata2 => 2,
DW_EH_PE_sdata4 => 4,
DW_EH_PE_sdata8 => 8,
_ => panic!()
}
}
pub enum EHAction {
None,
Cleanup(usize),
Catch(usize),
Terminate,
}
pub unsafe fn find_eh_action(lsda: *const u8, func_start: usize, ip: usize,
exn_name: CSlice<u8>) -> EHAction {
if lsda.is_null() {
return EHAction::None
}
let mut reader = DwarfReader::new(lsda);
let start_encoding = reader.read::<u8>();
// base address for landing pad offsets
let lpad_base = if start_encoding != DW_EH_PE_omit {
reader.read_encoded_pointer(start_encoding)
} else {
func_start
};
let ttype_encoding = reader.read::<u8>();
let ttype_encoding_size = encoding_size(ttype_encoding) as isize;
let class_info;
if ttype_encoding != DW_EH_PE_omit {
let class_info_offset = reader.read_uleb128();
class_info = reader.ptr.offset(class_info_offset as isize);
} else {
class_info = ptr::null();
}
assert!(!class_info.is_null());
let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128();
let action_table = reader.ptr.offset(call_site_table_length as isize);
while reader.ptr < action_table {
let cs_start = reader.read_encoded_pointer(call_site_encoding);
let cs_len = reader.read_encoded_pointer(call_site_encoding);
let cs_lpad = reader.read_encoded_pointer(call_site_encoding);
let cs_action = reader.read_uleb128();
if ip < func_start + cs_start {
// Callsite table is sorted by cs_start, so if we've passed the ip, we
// may stop searching.
break
}
if ip > func_start + cs_start + cs_len {
continue
}
if cs_lpad == 0 {
return EHAction::None
}
let lpad = lpad_base + cs_lpad;
if cs_action == 0 {
return EHAction::Cleanup(lpad)
}
let action_entry = action_table.offset((cs_action - 1) as isize);
let mut action_reader = DwarfReader::new(action_entry);
loop {
let type_info_offset = action_reader.read_sleb128() as isize;
let action_offset = action_reader.clone().read_sleb128() as isize;
assert!(type_info_offset >= 0);
if type_info_offset > 0 {
let type_info_ptr_ptr = class_info.offset(-type_info_offset * ttype_encoding_size);
let type_info_ptr = DwarfReader::new(type_info_ptr_ptr)
.read_encoded_pointer(ttype_encoding);
let type_info = *(type_info_ptr as *const CSlice<u8>);
if type_info.as_ref() == exn_name.as_ref() {
return EHAction::Catch(lpad)
}
if type_info.len() == 0 {
// This is a catch-all clause. We don't compare type_info_ptr with null here
// because, in PIC mode, the OR1K LLVM backend emits a literal zero
// encoded with DW_EH_PE_pcrel, which of course doesn't result in
// a proper null pointer.
return EHAction::Catch(lpad)
}
}
if action_offset == 0 {
break
} else {
action_reader.ptr = action_reader.ptr.offset(action_offset)
}
}
return EHAction::None
}
// the function has a personality but no landing pads; this is fine
EHAction::None
}

View File

@ -0,0 +1,88 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This is the Rust personality function, adapted for use in ARTIQ. We never actually panic
// from Rust or recover from Rust exceptions (there's nothing to catch the panics), but we
// need a personality function to step back through Rust frames in order to make a backtrace.
//
// By design, this personality function is only ever called in the search phase, although
// to keep things simple and close to upstream, it is not modified
#![allow(private_no_mangle_fns)]
use unwind as uw;
use libc::{c_int, uintptr_t};
use cslice::AsCSlice;
use dwarf::{self, EHAction};
// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
// and TargetLowering::getExceptionSelectorRegister() for each architecture,
// then mapped to DWARF register numbers via register definition tables
// (typically <arch>RegisterInfo.td, search for "DwarfRegNum").
// See also http://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register.
#[cfg(target_arch = "x86")]
const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX
#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
#[cfg(any(target_arch = "or1k"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
// The following code is based on GCC's C and C++ personality routines. For reference, see:
// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
#[lang = "eh_personality"]
#[no_mangle]
#[allow(unused)]
unsafe extern "C" fn rust_eh_personality(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
if version != 1 {
return uw::_URC_FATAL_PHASE1_ERROR;
}
let eh_action = match find_eh_action(context) {
Ok(action) => action,
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
};
if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
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, 0);
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
}
}
}
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context)
-> Result<EHAction, ()>
{
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let func = uw::_Unwind_GetRegionStart(context);
let ip = uw::_Unwind_GetIP(context);
Ok(dwarf::find_eh_action(lsda, func, ip, [].as_c_slice()))
}

View File

@ -0,0 +1,9 @@
#![feature(lang_items, panic_unwind, libc, unwind_attributes)]
#![no_std]
extern crate cslice;
extern crate unwind;
extern crate libc;
pub mod dwarf;
pub mod eh_rust;

View File

@ -18,7 +18,8 @@ failure_derive = { version = "0.1", default-features = false }
byteorder = { version = "1.0", default-features = false }
cslice = { version = "0.3" }
log = { version = "0.4", default-features = false }
managed = { version = "0.6", default-features = false, features = ["alloc", "map"] }
managed = { version = "= 0.7.0", default-features = false, features = ["alloc", "map"] }
eh = { path = "../libeh" }
unwind_backtrace = { path = "../libunwind_backtrace" }
io = { path = "../libio", features = ["byteorder"] }
alloc_list = { path = "../liballoc_list" }
@ -29,12 +30,12 @@ proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] }
[dependencies.fringe]
git = "https://github.com/m-labs/libfringe"
rev = "bd23494"
rev = "b8a6d8f"
default-features = false
features = ["alloc"]
[dependencies.smoltcp]
git = "https://github.com/m-labs/smoltcp"
rev = "181083f"
rev = "2139686" # NB: also change in libboard_misoc/Cargo.toml
default-features = false
features = ["alloc", "log", "proto-ipv4", "socket-tcp"]
features = ["rust-1.28", "alloc", "log", "proto-ipv4", "socket-tcp"]

View File

@ -1,7 +1,8 @@
#![feature(lang_items, alloc, global_allocator, try_from, nonzero, nll, needs_panic_runtime, asm)]
#![feature(lang_items, alloc, try_from, nonzero, asm,
panic_implementation, panic_info_message)]
#![no_std]
#![needs_panic_runtime]
extern crate eh;
#[macro_use]
extern crate alloc;
extern crate failure;
@ -250,18 +251,18 @@ fn startup_ethernet() {
net_device.reset_phy_if_any();
let net_device = {
use smoltcp::time::Instant;
use smoltcp::wire::PrettyPrinter;
use smoltcp::wire::EthernetFrame;
fn net_trace_writer(timestamp: u64, printer: PrettyPrinter<EthernetFrame<&[u8]>>) {
let seconds = timestamp / 1000;
let micros = timestamp % 1000 * 1000;
print!("\x1b[37m[{:6}.{:06}s]\n{}\x1b[0m\n", seconds, micros, printer)
fn net_trace_writer(timestamp: Instant, printer: PrettyPrinter<EthernetFrame<&[u8]>>) {
print!("\x1b[37m[{:6}.{:03}s]\n{}\x1b[0m\n",
timestamp.secs(), timestamp.millis(), printer)
}
fn net_trace_silent(_timestamp: u64, _printer: PrettyPrinter<EthernetFrame<&[u8]>>) {}
fn net_trace_silent(_timestamp: Instant, _printer: PrettyPrinter<EthernetFrame<&[u8]>>) {}
let net_trace_fn: fn(u64, PrettyPrinter<EthernetFrame<&[u8]>>);
let net_trace_fn: fn(Instant, PrettyPrinter<EthernetFrame<&[u8]>>);
match config::read_str("net_trace", |r| r.map(|s| s == "1")) {
Ok(true) => net_trace_fn = net_trace_writer,
_ => net_trace_fn = net_trace_silent
@ -299,7 +300,8 @@ fn startup_ethernet() {
{
let sockets = &mut *scheduler.sockets().borrow_mut();
loop {
match interface.poll(sockets, clock::get_ms()) {
let timestamp = smoltcp::time::Instant::from_millis(clock::get_ms() as i64);
match interface.poll(sockets, timestamp) {
Ok(true) => (),
Ok(false) => break,
Err(smoltcp::Error::Unrecognized) => (),
@ -373,13 +375,27 @@ pub extern fn abort() {
loop {}
}
#[no_mangle]
#[lang = "panic_fmt"]
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str,
line: u32, column: u32) -> ! {
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[lang = "oom"] // https://github.com/rust-lang/rust/issues/51540
pub fn oom(layout: core::alloc::Layout) -> ! {
panic!("heap view: {}\ncannot allocate layout: {:?}", unsafe { &ALLOC }, layout)
}
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_implementation]
pub fn panic_impl(info: &core::panic::PanicInfo) -> ! {
irq::set_ie(false);
println!("panic at {}:{}:{}: {}", file, line, column, args);
if let Some(location) = info.location() {
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("panic at unknown location");
}
if let Some(message) = info.message() {
println!("{}", message);
} else {
println!("");
}
println!("backtrace for software version {}:", csr::CONFIG_IDENTIFIER_STR);
let _ = unwind_backtrace::backtrace(|ip| {

View File

@ -64,28 +64,24 @@ impl Profile {
pub fn record_hit(&mut self, addr: Address) -> Result<(), ()> {
let mut hits = self.hits();
match hits.get_mut(&addr) {
Some(count) => *count = count.saturating_add(1),
None => {
if let Some(count) = hits.get_mut(&addr) {
return Ok(*count = count.saturating_add(1))
}
if let Err(_) = hits.insert(addr, 1) {
return Err(())
}
}
}
Ok(())
return Ok(())
}
#[allow(dead_code)]
pub fn record_edge(&mut self, caller: Address, callee: Address) -> Result<(), ()> {
let mut edges = self.edges();
match edges.get_mut(&(caller, callee)) {
Some(count) => *count = count.saturating_add(1),
None => {
if let Some(count) = edges.get_mut(&(caller, callee)) {
return Ok(*count = count.saturating_add(1))
}
if let Err(_) = edges.insert((caller, callee), 1) {
return Err(())
}
}
}
Ok(())
}
}

View File

@ -6,6 +6,7 @@ use core::cell::{Cell, RefCell};
use alloc::Vec;
use fringe::OwnedStack;
use fringe::generator::{Generator, Yielder, State as GeneratorState};
use smoltcp::time::Duration;
use smoltcp::Error as NetworkError;
use smoltcp::wire::IpEndpoint;
use smoltcp::socket::{SocketHandle, SocketRef};
@ -426,19 +427,19 @@ impl<'a> TcpStream<'a> {
}
pub fn timeout(&self) -> Option<u64> {
self.with_lower(|s| s.timeout())
self.with_lower(|s| s.timeout().as_ref().map(Duration::millis))
}
pub fn set_timeout(&self, value: Option<u64>) {
self.with_lower(|mut s| s.set_timeout(value))
self.with_lower(|mut s| s.set_timeout(value.map(Duration::from_millis)))
}
pub fn keep_alive(&self) -> Option<u64> {
self.with_lower(|s| s.keep_alive())
self.with_lower(|s| s.keep_alive().as_ref().map(Duration::millis))
}
pub fn set_keep_alive(&self, value: Option<u64>) {
self.with_lower(|mut s| s.set_keep_alive(value))
self.with_lower(|mut s| s.set_keep_alive(value.map(Duration::from_millis)))
}
pub fn close(&self) -> Result<(), Error> {

View File

@ -44,7 +44,7 @@ class TransferTest(ExperimentCase):
exp = self.create(_Transfer)
host_to_device_rate = exp.host_to_device()
print(host_to_device_rate, "B/s")
self.assertGreater(host_to_device_rate, 1.9e6)
self.assertGreater(host_to_device_rate, 1.8e6)
@unittest.skipUnless(artiq_low_latency,
"timings are dependent on CPU load and network conditions")
@ -52,7 +52,7 @@ class TransferTest(ExperimentCase):
exp = self.create(_Transfer)
device_to_host_rate = exp.device_to_host()
print(device_to_host_rate, "B/s")
self.assertGreater(device_to_host_rate, 1.9e6)
self.assertGreater(device_to_host_rate, 1.8e6)
class _KernelOverhead(EnvExperiment):

View File

@ -50,12 +50,17 @@ mod cslice {
}
}
#[path = "../../firmware/ksupport/eh.rs"]
pub mod eh;
#[path = "."]
pub mod eh {
#[path = "../../firmware/libeh/dwarf.rs"]
pub mod dwarf;
}
#[path = "../../firmware/ksupport/eh_artiq.rs"]
pub mod eh_artiq;
use std::{str, process};
fn terminate(exception: &eh::Exception, mut _backtrace: &mut [usize]) -> ! {
fn terminate(exception: &eh_artiq::Exception, mut _backtrace: &mut [usize]) -> ! {
println!("Uncaught {}: {} ({}, {}, {})",
str::from_utf8(exception.name.as_ref()).unwrap(),
str::from_utf8(exception.message.as_ref()).unwrap(),

View File

@ -30,7 +30,7 @@ if os.name == "posix":
support_build = os.path.join(root, "libartiq_support")
if subprocess.call(["rustc", os.path.join(support_build, "lib.rs"),
"--out-dir", support_build,
"-Cpanic=abort", "-g"]) != 0:
"-Cpanic=unwind", "-g"]) != 0:
lit_config.fatal("Unable to build JIT support library")
support_lib = os.path.join(support_build, "libartiq_support.so")

View File

@ -21,7 +21,7 @@ requirements:
- binutils-or1k-linux >=2.27
- llvm-or1k 6.0.0
- llvmlite-artiq 0.23.0.dev py35_4
- rust-core-or1k 1.26.0 21
- rust-core-or1k 1.28.0 21
- openocd 0.10.0 6
- lit
- outputcheck