From 0a35f68562a223dadd0d0b42821269739d5f8313 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Tue, 30 Jun 2020 17:22:44 +0800 Subject: [PATCH] Added libpanic_unwind/dwarf from rustc as libdwarf. --- src/Cargo.lock | 11 +++ src/Cargo.toml | 1 + src/libdwarf/Cargo.toml | 17 ++++ src/libdwarf/src/eh.rs | 200 ++++++++++++++++++++++++++++++++++++++ src/libdwarf/src/lib.rs | 74 ++++++++++++++ src/libdwarf/src/tests.rs | 19 ++++ src/runtime/Cargo.toml | 1 + 7 files changed, 323 insertions(+) create mode 100644 src/libdwarf/Cargo.toml create mode 100644 src/libdwarf/src/eh.rs create mode 100644 src/libdwarf/src/lib.rs create mode 100644 src/libdwarf/src/tests.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index 963b37a0..e257f1d7 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -82,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" @@ -378,6 +388,7 @@ dependencies = [ "byteorder", "core_io", "cslice", + "dwarf", "dyld", "fatfs", "futures", diff --git a/src/Cargo.toml b/src/Cargo.toml index 9386589c..9a53d0b7 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -3,6 +3,7 @@ members = [ "libc", "libdyld", "libcoreio", + "libdwarf", "libunwind", "runtime", "szl" diff --git a/src/libdwarf/Cargo.toml b/src/libdwarf/Cargo.toml new file mode 100644 index 00000000..d5335ea4 --- /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 00000000..8bb7ff00 --- /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 00000000..1ce25de2 --- /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 00000000..1644f370 --- /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/runtime/Cargo.toml b/src/runtime/Cargo.toml index c73a92d7..517837de 100644 --- a/src/runtime/Cargo.toml +++ b/src/runtime/Cargo.toml @@ -24,6 +24,7 @@ libsupport_zynq = { 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 }