blob: ff1c6aacd2f98284ec7859a550562dd14541691d [file] [log] [blame]
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(())
}
}