| use gimli::write::{Address, Writer}; |
| use gimli::{DW_EH_PE_omit, DW_EH_PE_uleb128, Encoding, LittleEndian}; |
| |
| pub(super) struct GccExceptTable { |
| pub call_sites: CallSiteTable, |
| pub actions: ActionTable, |
| pub type_info: TypeInfoTable, |
| } |
| |
| impl GccExceptTable { |
| pub(super) fn write<W: Writer>( |
| &self, |
| w: &mut W, |
| encoding: Encoding, |
| ) -> gimli::write::Result<()> { |
| // lpStartEncoding |
| w.write_u8(DW_EH_PE_omit.0)?; |
| // lpStart (omitted) |
| let type_info_padding = if self.type_info.type_info.is_empty() { |
| // ttypeEncoding |
| w.write_u8(DW_EH_PE_omit.0)?; |
| None |
| } else { |
| // ttypeEncoding |
| w.write_u8(self.type_info.ttype_encoding.0)?; |
| |
| // classInfoOffset |
| let class_info_offset_field_offset = w.len() as u64; |
| |
| // Note: The offset in classInfoOffset is relative to position right after classInfoOffset |
| // itself. |
| let class_info_offset_no_padding = self.call_sites.encoded_size() |
| + self.actions.encoded_size() |
| + self.type_info.encoded_size(encoding); |
| |
| let type_info_is_aligned = |type_info_padding: u64| { |
| (class_info_offset_field_offset |
| + gimli::leb128::write::uleb128_size( |
| class_info_offset_no_padding + type_info_padding, |
| ) as u64 |
| + self.call_sites.encoded_size() |
| + self.actions.encoded_size() |
| + type_info_padding) |
| .is_multiple_of(4) |
| }; |
| |
| let mut type_info_padding = 0; |
| while !type_info_is_aligned(type_info_padding) { |
| type_info_padding += 1; |
| } |
| |
| w.write_uleb128(class_info_offset_no_padding + type_info_padding)?; |
| |
| Some(type_info_padding) |
| }; |
| |
| // call site table |
| self.call_sites.write(w)?; |
| |
| // action table |
| self.actions.write(w)?; |
| |
| // align to 4 bytes |
| if let Some(type_info_padding) = type_info_padding { |
| for _ in 0..type_info_padding { |
| w.write_u8(0)?; |
| } |
| // In this case we calculated the expected padding amount and used it to write the |
| // classInfoOffset field. Assert that the expected value matched the actual value to catch |
| // any inconsistency. |
| assert!(w.len().is_multiple_of(4), "type_info must be aligned to 4 bytes"); |
| } else { |
| while !w.len().is_multiple_of(4) { |
| w.write_u8(0)?; |
| } |
| } |
| |
| // type_info |
| self.type_info.write(w, encoding)?; |
| |
| // exception specs (unused for rust) |
| |
| // align to 4 bytes |
| while !w.len().is_multiple_of(4) { |
| w.write_u8(0)?; |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| pub(super) struct CallSiteTable(pub Vec<CallSite>); |
| |
| impl CallSiteTable { |
| fn encoded_size(&self) -> u64 { |
| let mut len = LenWriter(0); |
| self.write(&mut len).unwrap(); |
| len.0 as u64 |
| } |
| |
| fn write<W: Writer>(&self, w: &mut W) -> gimli::write::Result<()> { |
| let callsite_table_length = self.0.iter().map(|call_site| call_site.encoded_size()).sum(); |
| |
| // callsiteEncoding |
| w.write_u8(DW_EH_PE_uleb128.0)?; |
| // callsiteTableLength |
| w.write_uleb128(callsite_table_length)?; |
| |
| for call_site in &self.0 { |
| call_site.write(w)?; |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| pub(super) struct CallSite { |
| pub start: u64, |
| pub length: u64, |
| pub landing_pad: u64, |
| pub action_entry: Option<ActionOffset>, |
| } |
| |
| impl CallSite { |
| fn encoded_size(&self) -> u64 { |
| let mut len = LenWriter(0); |
| self.write(&mut len).unwrap(); |
| len.0 as u64 |
| } |
| |
| fn write<W: Writer>(&self, w: &mut W) -> gimli::write::Result<()> { |
| w.write_uleb128(self.start)?; |
| w.write_uleb128(self.length)?; |
| w.write_uleb128(self.landing_pad)?; |
| w.write_uleb128(match self.action_entry { |
| Some(action_offset) => action_offset.0 + 1, |
| None => 0, |
| })?; |
| Ok(()) |
| } |
| } |
| |
| pub(super) struct ActionTable { |
| actions: Vec<Action>, |
| encoded_length: u64, |
| } |
| |
| impl ActionTable { |
| pub(super) fn new() -> ActionTable { |
| ActionTable { actions: vec![], encoded_length: 0 } |
| } |
| |
| pub(super) fn add(&mut self, action: Action) -> ActionOffset { |
| let id = ActionOffset(self.encoded_length); |
| self.encoded_length += action.encoded_size(self.encoded_length); |
| self.actions.push(action); |
| id |
| } |
| |
| fn encoded_size(&self) -> u64 { |
| let mut len = LenWriter(0); |
| self.write(&mut len).unwrap(); |
| len.0 as u64 |
| } |
| |
| fn write<W: Writer>(&self, w: &mut W) -> gimli::write::Result<()> { |
| let action_table_start = w.len() as u64; |
| for action in &self.actions { |
| action.write(w, w.len() as u64 - action_table_start)?; |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| #[derive(Copy, Clone)] |
| pub(super) struct ActionOffset(u64); |
| |
| pub(super) struct Action { |
| pub(super) kind: ActionKind, |
| pub(super) next_action: Option<ActionOffset>, |
| } |
| |
| impl Action { |
| fn encoded_size(&self, action_table_offset: u64) -> u64 { |
| let mut len = LenWriter(0); |
| self.write(&mut len, action_table_offset).unwrap(); |
| len.0 as u64 |
| } |
| |
| fn write<W: Writer>(&self, w: &mut W, action_table_offset: u64) -> gimli::write::Result<()> { |
| // ttypeIndex |
| let ttype_index = match self.kind { |
| ActionKind::Catch(type_info_id) => type_info_id.0 as i64 + 1, |
| }; |
| w.write_sleb128(ttype_index)?; |
| // actionOffset |
| let action_offset_field_offset = |
| action_table_offset + gimli::leb128::write::sleb128_size(ttype_index) as u64; |
| w.write_sleb128(match self.next_action { |
| Some(next_action_offset) => { |
| next_action_offset.0 as i64 - action_offset_field_offset as i64 |
| } |
| None => 0, |
| })?; |
| Ok(()) |
| } |
| } |
| |
| #[derive(Copy, Clone)] |
| pub(super) enum ActionKind { |
| Catch(TypeInfoId), |
| } |
| |
| pub(super) struct TypeInfoTable { |
| ttype_encoding: gimli::DwEhPe, |
| type_info: Vec<Address>, |
| } |
| |
| impl TypeInfoTable { |
| pub(super) fn new(ttype_encoding: gimli::DwEhPe) -> TypeInfoTable { |
| TypeInfoTable { ttype_encoding, type_info: vec![] } |
| } |
| |
| pub(super) fn add(&mut self, type_info: Address) -> TypeInfoId { |
| let id = TypeInfoId(self.type_info.len() as u64); |
| self.type_info.push(type_info); |
| id |
| } |
| |
| fn encoded_size(&self, encoding: Encoding) -> u64 { |
| let mut len = LenWriter(0); |
| self.write(&mut len, encoding).unwrap(); |
| len.0 as u64 |
| } |
| |
| fn write<W: Writer>(&self, w: &mut W, encoding: Encoding) -> gimli::write::Result<()> { |
| for &type_info in self.type_info.iter().rev() { |
| w.write_eh_pointer(type_info, self.ttype_encoding, encoding.address_size)?; |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| #[derive(Copy, Clone)] |
| pub(super) struct TypeInfoId(u64); |
| |
| struct LenWriter(usize); |
| |
| impl Writer for LenWriter { |
| type Endian = LittleEndian; |
| |
| fn endian(&self) -> LittleEndian { |
| LittleEndian |
| } |
| |
| fn len(&self) -> usize { |
| self.0 |
| } |
| |
| fn write(&mut self, bytes: &[u8]) -> gimli::write::Result<()> { |
| self.0 += bytes.len(); |
| Ok(()) |
| } |
| |
| fn write_at(&mut self, offset: usize, bytes: &[u8]) -> gimli::write::Result<()> { |
| assert!(offset + bytes.len() < self.0); |
| Ok(()) |
| } |
| } |