From acaf388dbb0f95d0095b34dc9c1dd5469baa775c Mon Sep 17 00:00:00 2001 From: pca006132 Date: Thu, 6 Jan 2022 01:54:34 +0800 Subject: [PATCH] eh_artiq: handle catch clauses appropriately --- default.nix | 2 +- src/Cargo.lock | 70 +++++++++++++++++ src/libdwarf/Cargo.toml | 1 + src/libdwarf/src/eh.rs | 150 +++++++++++++++++++++++++++++++----- src/libdwarf/src/lib.rs | 4 + src/runtime/src/eh_artiq.rs | 17 +++- 6 files changed, 218 insertions(+), 26 deletions(-) diff --git a/default.nix b/default.nix index ce21cbdb..bfb67e23 100644 --- a/default.nix +++ b/default.nix @@ -28,7 +28,7 @@ let name = "firmware"; src = ./src; - cargoSha256 = "sha256-uiwESZNwPdVnDkA1n0v1DQHp3rTazDkgIYscVTpgNq0="; + cargoSha256 = "sha256-uCqCeqXbTjTDEoZERPDb3kX+CTfzSJ/jGlrFxuKO8HQ="; nativeBuildInputs = [ pkgs.gnumake diff --git a/src/Cargo.lock b/src/Cargo.lock index f667c681..ab744cfe 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -29,6 +29,16 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "build_const" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" + +[[package]] +name = "build_zynq" +version = "0.0.0" + [[package]] name = "byteorder" version = "1.4.3" @@ -68,6 +78,15 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +dependencies = [ + "build_const", +] + [[package]] name = "cslice" version = "0.3.0" @@ -80,6 +99,7 @@ version = "0.0.0" dependencies = [ "cfg-if 0.1.10", "compiler_builtins", + "cslice", "libc", "unwind", ] @@ -192,6 +212,15 @@ dependencies = [ "proc-macro-nested", ] +[[package]] +name = "io" +version = "0.0.0" +dependencies = [ + "byteorder", + "core_io", + "libsupport_zynq", +] + [[package]] name = "libasync" version = "0.0.0" @@ -204,6 +233,26 @@ dependencies = [ "smoltcp", ] +[[package]] +name = "libboard_artiq" +version = "0.0.0" +dependencies = [ + "build_zynq", + "core_io", + "crc", + "embedded-hal", + "io", + "libasync", + "libboard_zynq", + "libconfig", + "libcortex_a9", + "libregister", + "log", + "log_buffer", + "nb 1.0.0", + "void", +] + [[package]] name = "libboard_zynq" version = "0.0.0" @@ -395,6 +444,7 @@ name = "runtime" version = "0.1.0" dependencies = [ "async-recursion", + "build_zynq", "byteorder", "core_io", "cslice", @@ -402,7 +452,9 @@ dependencies = [ "dyld", "embedded-hal", "futures", + "io", "libasync", + "libboard_artiq", "libboard_zynq", "libc", "libconfig", @@ -429,6 +481,24 @@ dependencies = [ "semver", ] +[[package]] +name = "satman" +version = "0.0.0" +dependencies = [ + "build_zynq", + "embedded-hal", + "libasync", + "libboard_artiq", + "libboard_zynq", + "libc", + "libconfig", + "libcortex_a9", + "libregister", + "libsupport_zynq", + "log", + "unwind", +] + [[package]] name = "semver" version = "0.1.20" diff --git a/src/libdwarf/Cargo.toml b/src/libdwarf/Cargo.toml index d5335ea4..4a8ae9e3 100644 --- a/src/libdwarf/Cargo.toml +++ b/src/libdwarf/Cargo.toml @@ -15,3 +15,4 @@ libc = { path = "../libc" } unwind = { path = "../libunwind" } compiler_builtins = "0.1.0" cfg-if = "0.1.8" +cslice = "0.3" diff --git a/src/libdwarf/src/eh.rs b/src/libdwarf/src/eh.rs index 8bb7ff00..d5ebd025 100644 --- a/src/libdwarf/src/eh.rs +++ b/src/libdwarf/src/eh.rs @@ -13,6 +13,7 @@ use crate::DwarfReader; use core::mem; +use cslice::CSlice; pub const DW_EH_PE_omit: u8 = 0xFF; pub const DW_EH_PE_absptr: u8 = 0x00; @@ -51,10 +52,46 @@ pub enum EHAction { pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); +fn size_of_encoded_value(encoding: u8) -> usize { + if encoding == DW_EH_PE_omit { + 0 + } else { + let encoding = encoding & 0x07; + match encoding { + DW_EH_PE_absptr => core::mem::size_of::<*const ()>(), + DW_EH_PE_udata2 => 2, + DW_EH_PE_udata4 => 4, + DW_EH_PE_udata8 => 8, + _ => unreachable!(), + } + } +} + +unsafe fn get_ttype_entry( + offset: usize, + encoding: u8, + ttype_base: usize, + ttype: *const u8, +) -> Result<*const u8, ()> { + let i = (offset * size_of_encoded_value(encoding)) as isize; + read_encoded_pointer_with_base( + &mut DwarfReader::new(ttype.offset(-i)), + // the DW_EH_PE_pcrel is a hack. + // It seems that the default encoding is absolute, but we have to take reallocation into + // account. Unsure if we can fix this in the compiler setting or if this would be affected + // by updating the compiler + encoding | DW_EH_PE_pcrel, + ttype_base, + ) + .map(|v| v as *const u8) +} + pub unsafe fn find_eh_action( lsda: *const u8, context: &EHContext<'_>, foreign_exception: bool, + name: *const u8, + len: usize, ) -> Result { if lsda.is_null() { return Ok(EHAction::None); @@ -72,10 +109,17 @@ pub unsafe fn find_eh_action( }; 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(); - } + // we do care about the type table + let ttype_offset = if ttype_encoding != DW_EH_PE_omit { + reader.read_uleb128() + } else { + 0 + }; + // for rust functions, it seems that there is no type table, so I just put whatever value here. + // we should not return an error, otherwise we would abort unwinding and cannot unwind through + // rust functions + let ttype_base = get_base(ttype_encoding, context).unwrap_or(1); + let ttype_table = reader.ptr.offset(ttype_offset as isize); let call_site_encoding = reader.read::(); let call_site_table_length = reader.read_uleb128(); @@ -94,11 +138,61 @@ pub unsafe fn find_eh_action( break; } if ip < func_start + cs_start + cs_len { + // https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/eh_personality.cc#L528 + let lpad = lpad_base + cs_lpad; if cs_lpad == 0 { + // no cleanups/handler + return Ok(EHAction::None); + } else if cs_action == 0 { + return Ok(EHAction::Cleanup(lpad)); + } else if foreign_exception { return Ok(EHAction::None); } else { - let lpad = lpad_base + cs_lpad; - return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)); + let mut saw_cleanup = false; + let mut action_record = action_table.offset(cs_action as isize - 1); + loop { + let mut reader = DwarfReader::new(action_record); + let ar_filter = reader.read_sleb128(); + action_record = reader.ptr; + let ar_disp = reader.read_sleb128(); + if ar_filter == 0 { + saw_cleanup = true; + } else if ar_filter > 0 { + let catch_type = get_ttype_entry( + ar_filter as usize, + ttype_encoding, + ttype_base, + ttype_table, + )?; + let clause_ptr = *(catch_type as *const *const CSlice); + if clause_ptr.is_null() { + return Ok(EHAction::Catch(lpad)); + } + let clause_name_ptr = (*clause_ptr).as_ptr(); + let clause_name_len = (*clause_ptr).len(); + if (clause_name_ptr == core::ptr::null() || + clause_name_ptr == name || + // somehow their name pointers might differ, but the content is the + // same + core::slice::from_raw_parts(clause_name_ptr, clause_name_len) == + core::slice::from_raw_parts(name, len)) + { + return Ok(EHAction::Catch(lpad)); + } + } else if ar_filter < 0 { + // FIXME: how to handle this? + break; + } + if ar_disp == 0 { + break; + } + action_record = action_record.offset((ar_disp as usize) as isize); + } + if saw_cleanup { + return Ok(EHAction::Cleanup(lpad)); + } else { + return Ok(EHAction::None); + } } } } @@ -106,7 +200,7 @@ pub unsafe fn find_eh_action( // So rather than returning EHAction::Terminate, we do this. Ok(EHAction::None) } else { - // SjLj version: + // SjLj version: (not yet modified) // 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 { @@ -146,18 +240,41 @@ fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) -> #[inline] fn round_up(unrounded: usize, align: usize) -> Result { - if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) } + if align.is_power_of_two() { + Ok((unrounded + align - 1) & !(align - 1)) + } else { + Err(()) + } +} + +fn get_base(encoding: u8, context: &EHContext<'_>) -> Result { + match encoding & 0x70 { + DW_EH_PE_absptr | DW_EH_PE_pcrel | DW_EH_PE_aligned => Ok(0), + DW_EH_PE_textrel => Ok((*context.get_text_start)()), + DW_EH_PE_datarel => Ok((*context.get_data_start)()), + DW_EH_PE_funcrel if context.func_start != 0 => Ok(context.func_start), + _ => return Err(()), + } } unsafe fn read_encoded_pointer( reader: &mut DwarfReader, context: &EHContext<'_>, encoding: u8, +) -> Result { + read_encoded_pointer_with_base(reader, encoding, get_base(encoding, context)?) +} + +unsafe fn read_encoded_pointer_with_base( + reader: &mut DwarfReader, + encoding: u8, + base: usize, ) -> Result { if encoding == DW_EH_PE_omit { return Err(()); } + let original_ptr = reader.ptr; // 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; @@ -177,19 +294,10 @@ unsafe fn read_encoded_pointer( _ => 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(()), + result += if (encoding & 0x70) == DW_EH_PE_pcrel { + original_ptr as usize + } else { + base }; if encoding & DW_EH_PE_indirect != 0 { diff --git a/src/libdwarf/src/lib.rs b/src/libdwarf/src/lib.rs index 1ce25de2..bbaa7154 100644 --- a/src/libdwarf/src/lib.rs +++ b/src/libdwarf/src/lib.rs @@ -26,6 +26,10 @@ impl DwarfReader { DwarfReader { ptr } } + pub unsafe fn offset(&mut self, offset: isize) { + self.ptr = self.ptr.offset(offset); + } + // 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 diff --git a/src/runtime/src/eh_artiq.rs b/src/runtime/src/eh_artiq.rs index 41938517..b6acd189 100644 --- a/src/runtime/src/eh_artiq.rs +++ b/src/runtime/src/eh_artiq.rs @@ -67,6 +67,8 @@ struct ExceptionInfo { unsafe fn find_eh_action( context: *mut uw::_Unwind_Context, foreign_exception: bool, + name: *const u8, + len: usize, ) -> Result { let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; let mut ip_before_instr: c_int = 0; @@ -79,7 +81,7 @@ unsafe fn find_eh_action( get_text_start: &|| uw::_Unwind_GetTextRelBase(context), get_data_start: &|| uw::_Unwind_GetDataRelBase(context), }; - eh::find_eh_action(lsda, &eh_context, foreign_exception) + eh::find_eh_action(lsda, &eh_context, foreign_exception, name, len) } pub unsafe fn artiq_personality(state: uw::_Unwind_State, @@ -120,11 +122,18 @@ pub unsafe fn artiq_personality(state: uw::_Unwind_State, let exception_class = (*exception_object).exception_class; let foreign_exception = exception_class != EXCEPTION_CLASS; - let eh_action = match find_eh_action(context, foreign_exception) { + let exception_info = &mut *(exception_object as *mut ExceptionInfo); + + let (name_ptr, len) = if foreign_exception || exception_info.exception.is_none() { + (core::ptr::null(), 0) + } else { + let name = (exception_info.exception.unwrap()).name; + (name.as_ptr(), name.len()) + }; + let eh_action = match find_eh_action(context, foreign_exception, name_ptr, len) { 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 { @@ -209,7 +218,7 @@ pub unsafe extern fn raise(exception: *const Exception) -> ! { INFLIGHT.handled = false; let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception); - assert!(result == uw::_URC_END_OF_STACK); + assert!(result == uw::_URC_FAILURE || result == uw::_URC_END_OF_STACK); INFLIGHT.backtrace_size = 0; // read backtrace