use crate::{metadata::typesystem::CilTypeRc, Result};
metadata_flags! {
pub struct ExceptionHandlerFlags(u16);
}
impl ExceptionHandlerFlags {
pub const EXCEPTION: Self = Self(0x0000);
pub const FILTER: Self = Self(0x0001);
pub const FINALLY: Self = Self(0x0002);
pub const FAULT: Self = Self(0x0004);
}
#[derive(Clone)]
pub struct ExceptionHandler {
pub flags: ExceptionHandlerFlags,
pub try_offset: u32,
pub try_length: u32,
pub handler_offset: u32,
pub handler_length: u32,
pub handler: Option<CilTypeRc>,
pub filter_offset: u32,
}
impl std::fmt::Debug for ExceptionHandler {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ExceptionHandler")
.field("flags", &self.flags)
.field("try_offset", &self.try_offset)
.field("try_length", &self.try_length)
.field("handler_offset", &self.handler_offset)
.field("handler_length", &self.handler_length)
.field("handler", &self.handler.as_ref().map(|t| t.token))
.field("filter_offset", &self.filter_offset)
.finish()
}
}
impl ExceptionHandler {
#[must_use]
pub fn get_filter_offset(&self) -> Option<u32> {
if self.flags.contains(ExceptionHandlerFlags::FILTER) {
Some(self.filter_offset)
} else {
None
}
}
#[must_use]
pub fn get_class_token(&self) -> Option<u32> {
if self.is_catch() && self.filter_offset != 0 {
Some(self.filter_offset)
} else {
None
}
}
#[must_use]
pub fn is_catch(&self) -> bool {
!self.flags.contains(ExceptionHandlerFlags::FILTER)
&& !self.flags.contains(ExceptionHandlerFlags::FINALLY)
&& !self.flags.contains(ExceptionHandlerFlags::FAULT)
}
#[must_use]
pub fn is_finally(&self) -> bool {
self.flags.contains(ExceptionHandlerFlags::FINALLY)
}
#[must_use]
pub fn is_fault(&self) -> bool {
self.flags.contains(ExceptionHandlerFlags::FAULT)
}
#[must_use]
pub fn is_filter(&self) -> bool {
self.flags.contains(ExceptionHandlerFlags::FILTER)
}
}
pub fn encode_exception_handlers(handlers: &[ExceptionHandler]) -> Result<Vec<u8>> {
if handlers.is_empty() {
return Ok(Vec::new());
}
let needs_fat_format = handlers.iter().any(|eh| {
eh.try_offset > 0xFFFF
|| eh.try_length > 0xFF
|| eh.handler_offset > 0xFFFF
|| eh.handler_length > 0xFF
});
let mut section = Vec::new();
if needs_fat_format {
let section_size = 4 + (handlers.len() * 24);
section.extend_from_slice(&[
0x41, 0x00, 0x00, ]);
let section_size_u32 = u32::try_from(section_size)
.map_err(|_| malformed_error!("Exception section size exceeds u32 range"))?;
section.extend_from_slice(§ion_size_u32.to_le_bytes()[..3]);
for eh in handlers {
section.extend_from_slice(&u32::from(eh.flags.bits()).to_le_bytes());
section.extend_from_slice(&eh.try_offset.to_le_bytes());
section.extend_from_slice(&eh.try_length.to_le_bytes());
section.extend_from_slice(&eh.handler_offset.to_le_bytes());
section.extend_from_slice(&eh.handler_length.to_le_bytes());
if eh.flags.contains(ExceptionHandlerFlags::FILTER) {
section.extend_from_slice(&eh.filter_offset.to_le_bytes());
} else if let Some(handler_type) = &eh.handler {
section.extend_from_slice(&handler_type.token.value().to_le_bytes());
} else if eh.flags == ExceptionHandlerFlags::EXCEPTION {
section.extend_from_slice(&eh.filter_offset.to_le_bytes());
} else {
section.extend_from_slice(&0u32.to_le_bytes());
}
}
} else {
let section_size = 4 + (handlers.len() * 12);
let section_size_u8 = u8::try_from(section_size).map_err(|_| {
malformed_error!("Exception section size exceeds u8 range for small format")
})?;
section.extend_from_slice(&[
0x01, section_size_u8, 0x00,
0x00, ]);
for eh in handlers {
section.extend_from_slice(&eh.flags.bits().to_le_bytes());
let try_offset_u16 = u16::try_from(eh.try_offset)
.map_err(|_| malformed_error!("Exception handler try_offset exceeds u16 range"))?;
section.extend_from_slice(&try_offset_u16.to_le_bytes());
let try_length_u8 = u8::try_from(eh.try_length)
.map_err(|_| malformed_error!("Exception handler try_length exceeds u8 range"))?;
section.push(try_length_u8);
let handler_offset_u16 = u16::try_from(eh.handler_offset).map_err(|_| {
malformed_error!("Exception handler handler_offset exceeds u16 range")
})?;
section.extend_from_slice(&handler_offset_u16.to_le_bytes());
let handler_length_u8 = u8::try_from(eh.handler_length).map_err(|_| {
malformed_error!("Exception handler handler_length exceeds u8 range")
})?;
section.push(handler_length_u8);
if eh.flags.contains(ExceptionHandlerFlags::FILTER) {
section.extend_from_slice(&eh.filter_offset.to_le_bytes());
} else if let Some(handler_type) = &eh.handler {
section.extend_from_slice(&handler_type.token.value().to_le_bytes());
} else if eh.flags == ExceptionHandlerFlags::EXCEPTION {
section.extend_from_slice(&eh.filter_offset.to_le_bytes());
} else {
section.extend_from_slice(&0u32.to_le_bytes());
}
}
}
while section.len() % 4 != 0 {
section.push(0);
}
Ok(section)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_exception_handlers_empty() {
let handlers = vec![];
let result = encode_exception_handlers(&handlers).unwrap();
assert!(result.is_empty());
}
#[test]
fn test_encode_exception_handlers_small_format() {
let handlers = vec![ExceptionHandler {
flags: ExceptionHandlerFlags::FINALLY,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
handler: None,
filter_offset: 0,
}];
let result = encode_exception_handlers(&handlers).unwrap();
assert_eq!(result.len(), 16);
assert_eq!(result[0], 0x01);
assert_eq!(result[1], 16);
}
#[test]
fn test_encode_exception_handlers_fat_format() {
let handlers = vec![ExceptionHandler {
flags: ExceptionHandlerFlags::EXCEPTION,
try_offset: 0x10000, try_length: 10,
handler_offset: 20,
handler_length: 5,
handler: None,
filter_offset: 0,
}];
let result = encode_exception_handlers(&handlers).unwrap();
assert_eq!(result.len(), 32);
assert_eq!(result[0], 0x41);
}
#[test]
fn test_encode_exception_handlers_filter() {
let handlers = vec![ExceptionHandler {
flags: ExceptionHandlerFlags::FILTER,
try_offset: 0,
try_length: 10,
handler_offset: 20,
handler_length: 5,
handler: None,
filter_offset: 15,
}];
let result = encode_exception_handlers(&handlers).unwrap();
assert_eq!(result.len(), 16); assert_eq!(result[0], 0x01); }
#[test]
fn test_exception_handler_get_filter_offset() {
let filter_handler = ExceptionHandler {
flags: ExceptionHandlerFlags::FILTER,
try_offset: 0,
try_length: 10,
handler_offset: 20,
handler_length: 5,
handler: None,
filter_offset: 15,
};
assert_eq!(filter_handler.get_filter_offset(), Some(15));
let finally_handler = ExceptionHandler {
flags: ExceptionHandlerFlags::FINALLY,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
handler: None,
filter_offset: 0,
};
assert_eq!(finally_handler.get_filter_offset(), None);
let exception_handler = ExceptionHandler {
flags: ExceptionHandlerFlags::EXCEPTION,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
handler: None,
filter_offset: 0x01000001, };
assert_eq!(exception_handler.get_filter_offset(), None);
}
#[test]
fn test_exception_handler_get_class_token() {
let unresolved_catch = ExceptionHandler {
flags: ExceptionHandlerFlags::EXCEPTION,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
handler: None,
filter_offset: 0x01000001, };
assert_eq!(unresolved_catch.get_class_token(), Some(0x01000001));
let resolved_catch = ExceptionHandler {
flags: ExceptionHandlerFlags::EXCEPTION,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
handler: None, filter_offset: 0,
};
assert_eq!(resolved_catch.get_class_token(), None);
let filter_handler = ExceptionHandler {
flags: ExceptionHandlerFlags::FILTER,
try_offset: 0,
try_length: 10,
handler_offset: 20,
handler_length: 5,
handler: None,
filter_offset: 15,
};
assert_eq!(filter_handler.get_class_token(), None);
}
#[test]
fn test_exception_handler_type_checks() {
let catch_handler = ExceptionHandler {
flags: ExceptionHandlerFlags::EXCEPTION,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
handler: None,
filter_offset: 0,
};
assert!(catch_handler.is_catch());
assert!(!catch_handler.is_finally());
assert!(!catch_handler.is_fault());
assert!(!catch_handler.is_filter());
let finally_handler = ExceptionHandler {
flags: ExceptionHandlerFlags::FINALLY,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
handler: None,
filter_offset: 0,
};
assert!(!finally_handler.is_catch());
assert!(finally_handler.is_finally());
assert!(!finally_handler.is_fault());
assert!(!finally_handler.is_filter());
let fault_handler = ExceptionHandler {
flags: ExceptionHandlerFlags::FAULT,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
handler: None,
filter_offset: 0,
};
assert!(!fault_handler.is_catch());
assert!(!fault_handler.is_finally());
assert!(fault_handler.is_fault());
assert!(!fault_handler.is_filter());
let filter_handler = ExceptionHandler {
flags: ExceptionHandlerFlags::FILTER,
try_offset: 0,
try_length: 10,
handler_offset: 20,
handler_length: 5,
handler: None,
filter_offset: 15,
};
assert!(!filter_handler.is_catch());
assert!(!filter_handler.is_finally());
assert!(!filter_handler.is_fault());
assert!(filter_handler.is_filter());
}
}