use llvm_native_core::value::ValueRef;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClauseType {
Catch,
Filter,
Cleanup,
}
#[derive(Debug, Clone)]
pub struct LandingPadClause {
pub clause_type: ClauseType,
pub exception_type: Option<ValueRef>,
pub filter_value: Option<i32>,
}
#[derive(Debug, Clone)]
pub struct LandingPad {
pub result_type: Option<ValueRef>,
pub clauses: Vec<LandingPadClause>,
pub is_cleanup: bool,
pub parent_block: Option<ValueRef>,
}
#[derive(Debug, Clone)]
pub struct InvokeInst {
pub callee: ValueRef,
pub arguments: Vec<ValueRef>,
pub normal_dest: ValueRef,
pub unwind_dest: ValueRef,
pub return_type: Option<ValueRef>,
}
#[derive(Debug, Clone)]
pub struct PersonalityFunction {
pub name: String,
pub func: Option<ValueRef>,
pub has_lsda: bool,
}
#[derive(Debug, Clone)]
pub struct CallSiteEntry {
pub cs_start: u32,
pub cs_length: u32,
pub cs_landing_pad: u32,
pub cs_action: u32,
}
#[derive(Debug, Clone)]
pub struct ActionRecord {
pub type_filter: i32,
pub next_action: i32,
}
#[derive(Debug, Clone)]
pub struct LSDAHeader {
pub lp_base_offset: u8,
pub ttype_encoding: u8,
pub ttype_offset: u32,
pub call_sites: Vec<CallSiteEntry>,
pub actions: Vec<ActionRecord>,
pub type_table: Vec<u32>,
}
#[derive(Debug, Clone)]
pub struct ExceptionTable {
pub lsda: LSDAHeader,
pub raw_bytes: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct EHFunctionState {
pub personality: Option<PersonalityFunction>,
pub landing_pads: Vec<LandingPad>,
pub invokes: Vec<InvokeInst>,
pub exception_table: Option<ExceptionTable>,
pub uses_eh: bool,
pub lsda_symbol: Option<String>,
}
impl EHFunctionState {
pub fn new() -> Self {
Self {
personality: None,
landing_pads: Vec::new(),
invokes: Vec::new(),
exception_table: None,
uses_eh: false,
lsda_symbol: None,
}
}
pub fn set_personality(&mut self, name: &str, func: Option<ValueRef>) {
self.personality = Some(PersonalityFunction {
name: name.to_string(),
func,
has_lsda: true,
});
self.uses_eh = true;
}
pub fn add_landing_pad(&mut self, lp: LandingPad) {
self.uses_eh = true;
self.landing_pads.push(lp);
}
pub fn add_invoke(&mut self, invoke: InvokeInst) {
self.uses_eh = true;
self.invokes.push(invoke);
}
pub fn has_invokes_to(&self, lp_block: &ValueRef) -> bool {
let lp_id = lp_block.borrow().vid;
self.invokes
.iter()
.any(|inv| inv.unwind_dest.borrow().vid == lp_id)
}
}
impl Default for EHFunctionState {
fn default() -> Self {
Self::new()
}
}
pub struct ExceptionTableBuilder {
call_sites: Vec<CallSiteEntry>,
actions: Vec<ActionRecord>,
type_offsets: HashMap<String, u32>,
next_type_offset: u32,
}
impl ExceptionTableBuilder {
pub fn new() -> Self {
Self {
call_sites: Vec::new(),
actions: Vec::new(),
type_offsets: HashMap::new(),
next_type_offset: 0,
}
}
pub fn add_call_site(&mut self, start: u32, length: u32, landing_pad: u32, action: u32) {
self.call_sites.push(CallSiteEntry {
cs_start: start,
cs_length: length,
cs_landing_pad: landing_pad,
cs_action: action,
});
}
pub fn add_catch_action(&mut self, type_name: &str) -> i32 {
if !self.type_offsets.contains_key(type_name) {
self.type_offsets
.insert(type_name.to_string(), self.next_type_offset);
self.next_type_offset += 1;
}
let type_index = *self.type_offsets.get(type_name).unwrap_or(&0);
let action_index = self.actions.len() as i32;
self.actions.push(ActionRecord {
type_filter: type_index as i32 + 1, next_action: 0,
});
action_index + 1 }
pub fn add_cleanup_action(&mut self) -> i32 {
let action_index = self.actions.len() as i32;
self.actions.push(ActionRecord {
type_filter: 0, next_action: 0,
});
action_index + 1
}
pub fn build_lsda(&self, _lp_base: u32) -> LSDAHeader {
LSDAHeader {
lp_base_offset: 0xFF, ttype_encoding: 0x9B, ttype_offset: 0, call_sites: self.call_sites.clone(),
actions: self.actions.clone(),
type_table: (0..self.type_offsets.len() as u32).collect(),
}
}
pub fn encode(&self, _function_start: u64) -> ExceptionTable {
let lsda = self.build_lsda(0);
let mut raw = Vec::new();
raw.push(0xFF); raw.push(0x9B); let cs_len = self.call_sites.len() as u64;
encode_uleb128(&mut raw, cs_len);
for cs in &self.call_sites {
encode_uleb128(&mut raw, cs.cs_start as u64);
encode_uleb128(&mut raw, cs.cs_length as u64);
encode_uleb128(&mut raw, cs.cs_landing_pad as u64);
encode_uleb128(&mut raw, cs.cs_action as u64);
}
for action in &self.actions {
encode_sleb128(&mut raw, action.type_filter as i64);
encode_sleb128(&mut raw, action.next_action as i64);
}
while raw.len() % 4 != 0 {
raw.push(0);
}
ExceptionTable {
lsda,
raw_bytes: raw,
}
}
}
impl Default for ExceptionTableBuilder {
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)]
pub mod personality_functions {
pub const GCC_CXX_PERSONALITY: &str = "__gxx_personality_v0";
pub const GCC_C_PERSONALITY: &str = "__gcc_personality_v0";
pub const SEH_PERSONALITY: &str = "__C_specific_handler";
pub const WASM_CXX_PERSONALITY: &str = "__gxx_wasm_personality_v0";
}
pub fn make_lsda_symbol(func_name: &str) -> String {
format!("__LSDA_{}", func_name)
}
pub fn gcc_except_table_section() -> &'static str {
".gcc_except_table"
}
pub fn function_uses_eh(func: &ValueRef) -> bool {
let f = func.borrow();
for op in &f.operands {
let bb = op.borrow();
if !bb.is_basic_block() {
continue;
}
for inst in &bb.operands {
let i = inst.borrow();
if i.is_instruction() {
let name = &i.name;
if name.contains("invoke") || name.contains("landingpad") || name.contains("resume")
{
return true;
}
}
}
}
false
}
fn encode_uleb128(out: &mut Vec<u8>, mut value: u64) {
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
out.push(byte);
if value == 0 {
break;
}
}
}
fn encode_sleb128(out: &mut Vec<u8>, mut value: i64) {
loop {
let mut byte = (value as u8) & 0x7F;
value >>= 7;
if (value == 0 && (byte & 0x40) == 0) || (value == -1 && (byte & 0x40) != 0) {
out.push(byte);
break;
}
byte |= 0x80;
out.push(byte);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EHPersonality {
GCC_CXX,
GCC_C,
GCC_ObjC,
MSVC_CXX,
MSVC_X86SEH,
Wasm_CXX,
Rust,
}
impl EHPersonality {
pub fn from_string(s: &str) -> Option<Self> {
match s {
"__gxx_personality_v0" => Some(Self::GCC_CXX),
"__gcc_personality_v0" => Some(Self::GCC_C),
"__gnu_objc_personality_v0" => Some(Self::GCC_ObjC),
"__CxxFrameHandler3" => Some(Self::MSVC_CXX),
"_except_handler3" => Some(Self::MSVC_X86SEH),
"__gxx_wasm_personality_v0" => Some(Self::Wasm_CXX),
"rust_eh_personality" => Some(Self::Rust),
_ => None,
}
}
pub fn to_fn_name(&self) -> &'static str {
match self {
Self::GCC_CXX => "__gxx_personality_v0",
Self::GCC_C => "__gcc_personality_v0",
Self::GCC_ObjC => "__gnu_objc_personality_v0",
Self::MSVC_CXX => "__CxxFrameHandler3",
Self::MSVC_X86SEH => "_except_handler3",
Self::Wasm_CXX => "__gxx_wasm_personality_v0",
Self::Rust => "rust_eh_personality",
}
}
}
#[derive(Debug, Clone)]
pub struct LandingPadInfo {
pub clauses: Vec<LandingPadClause>,
pub cleanup: bool,
pub filter_data: Vec<u32>,
}
impl LandingPadInfo {
pub fn parse(inst: &ValueRef) -> Option<Self> {
let i = inst.borrow();
if !i.is_instruction() {
return None;
}
let name = &i.name;
let is_cleanup = name.contains("cleanup");
let mut clauses = Vec::new();
let mut filter_data = Vec::new();
for op_ref in &i.operands {
let op = op_ref.borrow();
if op.is_constant() || !op.ty.is_void() {
clauses.push(LandingPadClause {
clause_type: ClauseType::Catch,
exception_type: Some(op_ref.clone()),
filter_value: None,
});
}
}
if is_cleanup {
clauses.push(LandingPadClause {
clause_type: ClauseType::Cleanup,
exception_type: None,
filter_value: None,
});
}
Some(Self {
clauses,
cleanup: is_cleanup,
filter_data,
})
}
pub fn add_catch(&mut self, ty: &llvm_native_core::types::Type) {
self.clauses.push(LandingPadClause {
clause_type: ClauseType::Catch,
exception_type: None,
filter_value: Some(ty.integer_bit_width() as i32),
});
}
pub fn add_filter(&mut self, types: &[llvm_native_core::types::Type]) {
for ty in types {
self.clauses.push(LandingPadClause {
clause_type: ClauseType::Filter,
exception_type: None,
filter_value: Some(ty.integer_bit_width() as i32),
});
}
}
}
pub struct InvokeLowering {
pub lowered_count: usize,
}
impl InvokeLowering {
pub fn new() -> Self {
Self { lowered_count: 0 }
}
pub fn lower(func: &ValueRef) -> usize {
let f = func.borrow();
let mut count = 0usize;
for op in &f.operands {
let bb = op.borrow();
if !bb.is_basic_block() {
continue;
}
for inst_val in &bb.operands {
let i = inst_val.borrow();
if i.is_instruction() && i.name.contains("invoke") {
count += 1;
}
}
}
count
}
pub fn create_dispatch_blocks(&self, func: &ValueRef) {
let f = func.borrow();
let _func_name = f.name.clone();
}
}
impl Default for InvokeLowering {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct WinEHInfo {
pub flags: u32,
pub try_levels: u32,
pub unwind_offset: u32,
pub handler_type: u32,
}
#[derive(Debug, Clone)]
pub struct WinEHResult {
pub xdata: Vec<u8>,
pub pdata: Vec<u8>,
pub num_unwind_codes: usize,
}
impl WinEHInfo {
pub fn generate(func: &ValueRef) -> WinEHResult {
let f = func.borrow();
let _func_name = f.name.clone();
let pdata = Vec::new();
let xdata = Vec::new();
WinEHResult {
xdata,
pdata,
num_unwind_codes: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct DwarfEHInfo {
pub fde_count: usize,
pub cie_count: usize,
pub eh_frame_size: usize,
pub lsda_offsets: HashMap<String, u32>,
}
#[derive(Debug, Clone)]
pub struct DwarfEHResult {
pub eh_frame: Vec<u8>,
pub gcc_except_table: Vec<u8>,
pub num_fdes: usize,
}
impl DwarfEHInfo {
pub fn generate(func: &ValueRef) -> DwarfEHResult {
let f = func.borrow();
let _func_name = f.name.clone();
let eh_frame = Vec::new();
let gcc_except_table = Vec::new();
DwarfEHResult {
eh_frame,
gcc_except_table,
num_fdes: 0,
}
}
}
pub struct EHFrameBuilder {
cie_bytes: Vec<u8>,
fde_bytes: Vec<u8>,
fde_count: usize,
}
impl EHFrameBuilder {
pub fn new() -> Self {
Self {
cie_bytes: Vec::new(),
fde_bytes: Vec::new(),
fde_count: 0,
}
}
pub fn add_cie(&mut self, func: &ValueRef, personality: &EHPersonality) {
let f = func.borrow();
let _func_name = f.name.clone();
let mut cie = Vec::new();
cie.extend_from_slice(&[0u8; 4]);
cie.extend_from_slice(&0u32.to_le_bytes());
cie.push(1);
let aug = format!("zPLR\0");
cie.extend_from_slice(aug.as_bytes());
cie.push(1);
encode_sleb128(&mut cie, -8);
cie.push(16);
encode_uleb128(&mut cie, 5);
cie.push(0x9B);
cie.extend_from_slice(&0u32.to_le_bytes());
cie.push(0x1B);
let cie_len = (cie.len() - 4) as u32;
cie[0..4].copy_from_slice(&cie_len.to_le_bytes());
self.cie_bytes = cie;
}
pub fn add_fde(&mut self, func: &ValueRef, landing_pads: &[LandingPadInfo]) {
let f = func.borrow();
let _func_name = f.name.clone();
let mut fde = Vec::new();
fde.extend_from_slice(&[0u8; 4]);
fde.extend_from_slice(&(self.cie_bytes.len() as u32).to_le_bytes());
fde.extend_from_slice(&0u64.to_le_bytes());
fde.extend_from_slice(&0u64.to_le_bytes());
let lsda_count = landing_pads.len() as u64;
encode_uleb128(&mut fde, lsda_count);
for _ in landing_pads {
fde.extend_from_slice(&0u32.to_le_bytes());
}
let fde_len = (fde.len() - 4) as u32;
fde[0..4].copy_from_slice(&fde_len.to_le_bytes());
self.fde_bytes.extend_from_slice(&fde);
self.fde_count += 1;
}
pub fn emit_eh_frame(&self) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&self.cie_bytes);
out.extend_from_slice(&self.fde_bytes);
out
}
}
impl Default for EHFrameBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SEHScopeKind {
Try,
Except,
Finally,
}
#[derive(Debug, Clone)]
pub struct SEHScope {
pub kind: SEHScopeKind,
pub try_start: u64,
pub try_end: u64,
pub handler_pc: u64,
pub filter_pc: Option<u64>,
pub level: u32,
}
#[derive(Debug, Clone)]
pub struct SEHUnwindInfo {
pub begin_address: u64,
pub end_address: u64,
pub unwind_info_address: u64,
pub unwind_codes: Vec<u8>,
pub handler_rva: Option<u32>,
}
impl SEHUnwindInfo {
pub fn new(begin: u64, end: u64) -> Self {
Self {
begin_address: begin,
end_address: end,
unwind_info_address: 0,
unwind_codes: Vec::new(),
handler_rva: None,
}
}
pub fn push_register(&mut self, reg: u8) {
self.unwind_codes.push(reg);
}
pub fn set_handler(&mut self, handler_rva: u32) {
self.handler_rva = Some(handler_rva);
}
pub fn size(&self) -> usize {
8 + self.unwind_codes.len() * 2
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WasmEHInst {
Try,
Catch,
CatchAll,
Throw,
Rethrow,
EndTry,
}
#[derive(Debug, Clone)]
pub struct WasmTag {
pub index: u32,
pub param_types: Vec<String>,
pub is_imported: bool,
}
impl WasmTag {
pub fn new(index: u32) -> Self {
Self {
index,
param_types: Vec::new(),
is_imported: false,
}
}
pub fn with_params(mut self, params: Vec<String>) -> Self {
self.param_types = params;
self
}
}
pub struct WasmEHBuilder {
pub tags: Vec<WasmTag>,
try_depth: u32,
try_stack: Vec<u32>,
}
impl WasmEHBuilder {
pub fn new() -> Self {
Self {
tags: Vec::new(),
try_depth: 0,
try_stack: Vec::new(),
}
}
pub fn add_tag(&mut self, params: Vec<String>) -> WasmTag {
let tag = WasmTag::new(self.tags.len() as u32).with_params(params);
self.tags.push(tag.clone());
tag
}
pub fn begin_try(&mut self) -> u32 {
self.try_depth += 1;
self.try_stack.push(self.try_depth);
self.try_depth
}
pub fn begin_catch(&mut self) -> u32 {
self.try_depth
}
pub fn begin_catch_all(&mut self) -> u32 {
self.try_depth
}
pub fn end_try(&mut self) {
if self.try_depth > 0 {
self.try_depth -= 1;
}
self.try_stack.pop();
}
pub fn is_in_try(&self) -> bool {
self.try_depth > 0
}
}
impl Default for WasmEHBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ExtendedLandingPad {
pub result_type: String,
pub clauses: Vec<LandingPadClause>,
pub is_cleanup: bool,
pub is_catch_all: bool,
pub parent_block: Option<ValueRef>,
}
impl ExtendedLandingPad {
pub fn new(result_type: &str) -> Self {
Self {
result_type: result_type.to_string(),
clauses: Vec::new(),
is_cleanup: false,
is_catch_all: false,
parent_block: None,
}
}
pub fn with_cleanup(mut self) -> Self {
self.is_cleanup = true;
self
}
pub fn with_catch_all(mut self) -> Self {
self.is_catch_all = true;
self
}
pub fn add_catch(mut self, exception_type: &str) -> Self {
let _ = exception_type;
self.clauses.push(LandingPadClause {
clause_type: ClauseType::Catch,
exception_type: None,
filter_value: None,
});
self
}
pub fn add_filter(mut self, filter_value: Option<i32>) -> Self {
self.clauses.push(LandingPadClause {
clause_type: ClauseType::Filter,
exception_type: None,
filter_value,
});
self
}
}
pub mod personality_functions_ext {
pub const GCC_CXX_PERSONALITY_V0: &str = "__gxx_personality_v0";
pub const GCC_C_PERSONALITY_V0: &str = "__gcc_personality_v0";
pub const GCC_OBJC_PERSONALITY_V0: &str = "__objc_personality_v0";
pub const MSVC_CXX_FRAME_HANDLER3: &str = "__CxxFrameHandler3";
pub const MSVC_CXX_FRAME_HANDLER: &str = "__CxxFrameHandler";
pub const EXCEPT_HANDLER3: &str = "_except_handler3";
pub const EXCEPT_HANDLER4: &str = "_except_handler4";
pub const WASM_CXX_PERSONALITY_V0: &str = "__gxx_wasm_personality_v0";
pub const RUST_EH_PERSONALITY: &str = "rust_eh_personality";
}
#[allow(dead_code)]
pub mod lsda_encoding {
pub const DW_EH_PE_ABS: 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;
pub const DW_EH_PE_OMIT: u8 = 0xff;
}
pub fn select_personality_for_triple(target_triple: &str) -> &'static str {
let t = target_triple.to_lowercase();
if t.contains("windows") || t.contains("msvc") {
if t.contains("x86_64") || t.contains("aarch64") {
personality_functions_ext::MSVC_CXX_FRAME_HANDLER3
} else {
personality_functions_ext::EXCEPT_HANDLER4
}
} else if t.contains("wasm") {
personality_functions_ext::WASM_CXX_PERSONALITY_V0
} else {
personality_functions_ext::GCC_CXX_PERSONALITY_V0
}
}
pub fn encode_eh_value(encoding: u8, value: i64) -> Vec<u8> {
let mut out = Vec::new();
let format = encoding & 0x0f;
match format {
lsda_encoding::DW_EH_PE_ULEB128 => {
out.extend_from_slice(&encode_uleb128_ext(value as u64));
}
lsda_encoding::DW_EH_PE_SLEB128 => {
out.extend_from_slice(&encode_sleb128_ext(value));
}
lsda_encoding::DW_EH_PE_UDATA2 => {
out.extend_from_slice(&(value as u16).to_le_bytes());
}
lsda_encoding::DW_EH_PE_UDATA4 => {
out.extend_from_slice(&(value as u32).to_le_bytes());
}
lsda_encoding::DW_EH_PE_UDATA8 => {
out.extend_from_slice(&(value as u64).to_le_bytes());
}
lsda_encoding::DW_EH_PE_SDATA2 => {
out.extend_from_slice(&(value as i16).to_le_bytes());
}
lsda_encoding::DW_EH_PE_SDATA4 => {
out.extend_from_slice(&(value as i32).to_le_bytes());
}
lsda_encoding::DW_EH_PE_SDATA8 => {
out.extend_from_slice(&value.to_le_bytes());
}
lsda_encoding::DW_EH_PE_ABS => {
out.extend_from_slice(&(value as u64).to_le_bytes());
}
lsda_encoding::DW_EH_PE_OMIT => {
}
_ => {
out.extend_from_slice(&(value as u32).to_le_bytes());
}
}
out
}
fn encode_uleb128_ext(mut value: u64) -> Vec<u8> {
let mut out = Vec::new();
loop {
let mut byte = (value & 0x7f) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
out.push(byte);
if value == 0 {
break;
}
}
out
}
fn encode_sleb128_ext(mut value: i64) -> Vec<u8> {
let mut out = Vec::new();
loop {
let mut byte = (value & 0x7f) as u8;
value >>= 7;
let more = !((value == 0 && byte & 0x40 == 0) || (value == -1 && byte & 0x40 != 0));
if more {
byte |= 0x80;
}
out.push(byte);
if !more {
break;
}
}
out
}
#[derive(Debug, Clone)]
pub struct EHFrameInfo {
pub fde_count: u32,
pub cie_count: u32,
pub eh_frame_size: u64,
pub except_table_size: u64,
pub lsda_offsets: HashMap<String, u64>,
pub personality_has_lsda: bool,
}
impl EHFrameInfo {
pub fn new() -> Self {
Self {
fde_count: 0,
cie_count: 0,
eh_frame_size: 0,
except_table_size: 0,
lsda_offsets: HashMap::new(),
personality_has_lsda: true,
}
}
pub fn add_fde(&mut self, func_name: &str, lsda_offset: u64) {
self.fde_count += 1;
self.lsda_offsets.insert(func_name.to_string(), lsda_offset);
}
pub fn total_size(&self) -> u64 {
self.eh_frame_size + self.except_table_size
}
}
impl Default for EHFrameInfo {
fn default() -> Self {
Self::new()
}
}
pub fn match_exception_type(exception_type: &str, catch_type: &str) -> bool {
if catch_type == "..." || catch_type.is_empty() {
return true; }
if exception_type == catch_type {
return true;
}
false
}
pub fn find_catch_handler(exception_type: &str, clauses: &[LandingPadClause]) -> Option<usize> {
for (i, clause) in clauses.iter().enumerate() {
if clause.clause_type == ClauseType::Catch {
let _ = exception_type;
let _ = &clause.exception_type;
return Some(i);
}
}
None
}
pub fn compute_action_offset(type_index: usize, type_table: &[String]) -> Option<usize> {
if type_index < type_table.len() {
Some(type_index * 2)
} else {
None
}
}
pub fn eh_section_for_target(target_triple: &str) -> &'static str {
let t = target_triple.to_lowercase();
if t.contains("darwin") || t.contains("macos") {
"__TEXT,__gcc_except_tab"
} else if t.contains("windows") || t.contains("msvc") {
".xdata"
} else {
".gcc_except_table"
}
}
pub fn eh_frame_section_for_target(target_triple: &str) -> &'static str {
let t = target_triple.to_lowercase();
if t.contains("darwin") || t.contains("macos") {
"__TEXT,__eh_frame"
} else {
".eh_frame"
}
}
#[cfg(test)]
mod tests {
use super::*;
use llvm_native_core::basic_block::new_basic_block;
use llvm_native_core::function::new_function;
use llvm_native_core::instruction;
use llvm_native_core::types::Type;
fn build_simple_func(name: &str) -> ValueRef {
let func = new_function(name, Type::void(), &[]);
let entry = new_basic_block("entry");
entry.borrow_mut().push_operand(instruction::ret_void());
func.borrow_mut().push_operand(entry.clone());
func
}
#[test]
fn test_eh_state_create() {
let state = EHFunctionState::new();
assert!(!state.uses_eh);
assert!(state.personality.is_none());
assert!(state.landing_pads.is_empty());
}
#[test]
fn test_eh_state_set_personality() {
let mut state = EHFunctionState::new();
state.set_personality("__gxx_personality_v0", None);
assert!(state.uses_eh);
assert!(state.personality.is_some());
assert_eq!(state.personality.unwrap().name, "__gxx_personality_v0");
}
#[test]
fn test_eh_state_add_landing_pad() {
let mut state = EHFunctionState::new();
let lp = LandingPad {
result_type: None,
clauses: vec![],
is_cleanup: true,
parent_block: None,
};
state.add_landing_pad(lp);
assert!(state.uses_eh);
assert_eq!(state.landing_pads.len(), 1);
}
#[test]
fn test_eh_state_add_invoke() {
let mut state = EHFunctionState::new();
let normal = new_basic_block("normal");
let unwind = new_basic_block("unwind");
let invoke = InvokeInst {
callee: build_simple_func("callee"),
arguments: vec![],
normal_dest: normal,
unwind_dest: unwind.clone(),
return_type: None,
};
state.add_invoke(invoke);
assert!(state.uses_eh);
assert_eq!(state.invokes.len(), 1);
assert!(state.has_invokes_to(&unwind));
}
#[test]
fn test_table_builder_create() {
let builder = ExceptionTableBuilder::new();
assert!(builder.call_sites.is_empty());
assert!(builder.actions.is_empty());
}
#[test]
fn test_table_builder_add_call_site() {
let mut builder = ExceptionTableBuilder::new();
builder.add_call_site(0x10, 0x20, 0x100, 1);
assert_eq!(builder.call_sites.len(), 1);
assert_eq!(builder.call_sites[0].cs_start, 0x10);
assert_eq!(builder.call_sites[0].cs_length, 0x20);
}
#[test]
fn test_table_builder_add_catch_action() {
let mut builder = ExceptionTableBuilder::new();
let action = builder.add_catch_action("std::runtime_error");
assert!(action > 0);
assert_eq!(builder.actions.len(), 1);
assert!(builder.actions[0].type_filter > 0);
}
#[test]
fn test_table_builder_add_cleanup_action() {
let mut builder = ExceptionTableBuilder::new();
let action = builder.add_cleanup_action();
assert!(action > 0);
assert_eq!(builder.actions.len(), 1);
assert_eq!(builder.actions[0].type_filter, 0);
}
#[test]
fn test_table_builder_multiple_call_sites() {
let mut builder = ExceptionTableBuilder::new();
builder.add_call_site(0, 50, 100, 0);
builder.add_call_site(50, 30, 200, 1);
builder.add_call_site(80, 20, 0, 2);
assert_eq!(builder.call_sites.len(), 3);
}
#[test]
fn test_table_builder_build_lsda() {
let mut builder = ExceptionTableBuilder::new();
builder.add_call_site(0, 10, 100, 1);
builder.add_catch_action("int");
let lsda = builder.build_lsda(0);
assert_eq!(lsda.call_sites.len(), 1);
assert_eq!(lsda.actions.len(), 1);
}
#[test]
fn test_table_builder_encode() {
let mut builder = ExceptionTableBuilder::new();
builder.add_call_site(0, 10, 0, 0);
let table = builder.encode(0x1000);
assert!(!table.raw_bytes.is_empty());
}
#[test]
fn test_table_builder_encode_multiple_types() {
let mut builder = ExceptionTableBuilder::new();
builder.add_call_site(0, 20, 100, 1);
builder.add_catch_action("int");
builder.add_catch_action("float");
builder.add_cleanup_action();
let table = builder.encode(0);
assert!(!table.raw_bytes.is_empty());
assert_eq!(builder.actions.len(), 3);
}
#[test]
fn test_personality_creation() {
let p = PersonalityFunction {
name: "__gxx_personality_v0".into(),
func: None,
has_lsda: true,
};
assert_eq!(p.name, "__gxx_personality_v0");
assert!(p.has_lsda);
}
#[test]
fn test_make_lsda_symbol() {
let sym = make_lsda_symbol("my_function");
assert_eq!(sym, "__LSDA_my_function");
}
#[test]
fn test_gcc_except_table_section() {
assert_eq!(gcc_except_table_section(), ".gcc_except_table");
}
#[test]
fn test_function_uses_eh_false_for_simple() {
let func = build_simple_func("no_eh");
assert!(!function_uses_eh(&func));
}
#[test]
fn test_clause_types() {
let catch = LandingPadClause {
clause_type: ClauseType::Catch,
exception_type: None,
filter_value: None,
};
assert_eq!(catch.clause_type, ClauseType::Catch);
let cleanup = LandingPadClause {
clause_type: ClauseType::Cleanup,
exception_type: None,
filter_value: None,
};
assert_eq!(cleanup.clause_type, ClauseType::Cleanup);
}
#[test]
fn test_landing_pad_create() {
let lp = LandingPad {
result_type: None,
clauses: vec![],
is_cleanup: false,
parent_block: None,
};
assert!(!lp.is_cleanup);
assert!(lp.clauses.is_empty());
}
#[test]
fn test_landing_pad_with_clauses() {
let lp = LandingPad {
result_type: None,
clauses: vec![
LandingPadClause {
clause_type: ClauseType::Catch,
exception_type: None,
filter_value: None,
},
LandingPadClause {
clause_type: ClauseType::Cleanup,
exception_type: None,
filter_value: None,
},
],
is_cleanup: true,
parent_block: None,
};
assert!(lp.is_cleanup);
assert_eq!(lp.clauses.len(), 2);
}
#[test]
fn test_invoke_create() {
let normal = new_basic_block("cont");
let unwind = new_basic_block("lpad");
let invoke = InvokeInst {
callee: build_simple_func("foo"),
arguments: vec![],
normal_dest: normal,
unwind_dest: unwind,
return_type: None,
};
assert!(invoke.arguments.is_empty());
}
#[test]
fn test_lsda_symbols_are_unique() {
let s1 = make_lsda_symbol("foo");
let s2 = make_lsda_symbol("bar");
assert_ne!(s1, s2);
}
#[test]
fn test_full_eh_pipeline() {
let mut state = EHFunctionState::new();
state.set_personality("__gxx_personality_v0", None);
let lp = LandingPad {
result_type: None,
clauses: vec![LandingPadClause {
clause_type: ClauseType::Catch,
exception_type: None,
filter_value: None,
}],
is_cleanup: false,
parent_block: None,
};
state.add_landing_pad(lp);
let normal = new_basic_block("normal_dest");
let unwind = new_basic_block("unwind_dest");
let invoke = InvokeInst {
callee: build_simple_func("may_throw"),
arguments: vec![],
normal_dest: normal,
unwind_dest: unwind.clone(),
return_type: None,
};
state.add_invoke(invoke);
let mut builder = ExceptionTableBuilder::new();
builder.add_call_site(0, 10, 100, 1);
builder.add_catch_action("int");
let table = builder.encode(0x1000);
assert!(state.uses_eh);
assert_eq!(state.invokes.len(), 1);
assert_eq!(state.landing_pads.len(), 1);
assert!(!table.raw_bytes.is_empty());
assert!(state.has_invokes_to(&unwind));
}
#[test]
fn test_eh_state_default() {
let state = EHFunctionState::default();
assert!(!state.uses_eh);
assert_eq!(state.invokes.len(), 0);
}
#[test]
fn test_personality_standard_names() {
assert_eq!(
personality_functions::GCC_CXX_PERSONALITY,
"__gxx_personality_v0"
);
assert_eq!(
personality_functions::GCC_C_PERSONALITY,
"__gcc_personality_v0"
);
}
#[test]
fn test_function_uses_eh_default_false() {
let func = build_simple_func("test_fn");
assert!(!function_uses_eh(&func));
}
#[test]
fn test_eh_personality_from_string() {
assert_eq!(
EHPersonality::from_string("__gxx_personality_v0"),
Some(EHPersonality::GCC_CXX)
);
assert_eq!(
EHPersonality::from_string("__gcc_personality_v0"),
Some(EHPersonality::GCC_C)
);
assert_eq!(
EHPersonality::from_string("__CxxFrameHandler3"),
Some(EHPersonality::MSVC_CXX)
);
assert_eq!(
EHPersonality::from_string("_except_handler3"),
Some(EHPersonality::MSVC_X86SEH)
);
assert_eq!(
EHPersonality::from_string("__gxx_wasm_personality_v0"),
Some(EHPersonality::Wasm_CXX)
);
assert_eq!(
EHPersonality::from_string("rust_eh_personality"),
Some(EHPersonality::Rust)
);
assert_eq!(
EHPersonality::from_string("__gnu_objc_personality_v0"),
Some(EHPersonality::GCC_ObjC)
);
}
#[test]
fn test_eh_personality_from_string_unknown() {
assert_eq!(EHPersonality::from_string("unknown_personality"), None);
assert_eq!(EHPersonality::from_string(""), None);
}
#[test]
fn test_eh_personality_to_fn_name() {
assert_eq!(EHPersonality::GCC_CXX.to_fn_name(), "__gxx_personality_v0");
assert_eq!(EHPersonality::GCC_C.to_fn_name(), "__gcc_personality_v0");
assert_eq!(EHPersonality::MSVC_CXX.to_fn_name(), "__CxxFrameHandler3");
assert_eq!(
EHPersonality::Wasm_CXX.to_fn_name(),
"__gxx_wasm_personality_v0"
);
assert_eq!(EHPersonality::Rust.to_fn_name(), "rust_eh_personality");
}
#[test]
fn test_eh_personality_roundtrip() {
for &p in &[
EHPersonality::GCC_CXX,
EHPersonality::GCC_C,
EHPersonality::MSVC_CXX,
EHPersonality::Rust,
EHPersonality::Wasm_CXX,
] {
let name = p.to_fn_name();
let parsed = EHPersonality::from_string(name);
assert_eq!(parsed, Some(p), "Roundtrip failed for {:?}", p);
}
}
#[test]
fn test_landing_pad_info_parse() {
let mut val = llvm_native_core::value::Value::new(Type::void());
val.opcode = Some(llvm_native_core::opcode::Opcode::LandingPad);
val.subclass = llvm_native_core::value::SubclassKind::Instruction;
let lp_inst = llvm_native_core::value::valref(val);
let _info = LandingPadInfo::parse(&lp_inst);
}
#[test]
fn test_landing_pad_info_add_catch() {
let mut info = LandingPadInfo {
clauses: vec![],
cleanup: false,
filter_data: vec![],
};
info.add_catch(&Type::i32());
assert_eq!(info.clauses.len(), 1);
assert_eq!(info.clauses[0].clause_type, ClauseType::Catch);
}
#[test]
fn test_landing_pad_info_add_filter() {
let mut info = LandingPadInfo {
clauses: vec![],
cleanup: false,
filter_data: vec![],
};
info.add_filter(&[Type::i32(), Type::i64()]);
assert_eq!(info.clauses.len(), 2);
for clause in &info.clauses {
assert_eq!(clause.clause_type, ClauseType::Filter);
}
}
#[test]
fn test_invoke_lowering_create() {
let lowering = InvokeLowering::new();
assert_eq!(lowering.lowered_count, 0);
}
#[test]
fn test_invoke_lowering_lower() {
let func = build_simple_func("invoke_test");
let count = InvokeLowering::lower(&func);
assert_eq!(count, 0);
}
#[test]
fn test_invoke_lowering_create_dispatch() {
let lowering = InvokeLowering::new();
let func = build_simple_func("dispatch_test");
lowering.create_dispatch_blocks(&func);
}
#[test]
fn test_win_eh_info_generate() {
let func = build_simple_func("seh_test");
let result = WinEHInfo::generate(&func);
assert_eq!(result.num_unwind_codes, 0);
}
#[test]
fn test_dwarf_eh_info_generate() {
let func = build_simple_func("dwarf_test");
let result = DwarfEHInfo::generate(&func);
assert_eq!(result.num_fdes, 0);
}
#[test]
fn test_eh_frame_builder_create() {
let builder = EHFrameBuilder::new();
assert_eq!(builder.fde_count, 0);
let eh_frame = builder.emit_eh_frame();
assert!(eh_frame.is_empty());
}
#[test]
fn test_eh_frame_builder_add_cie_and_fde() {
let mut builder = EHFrameBuilder::new();
let func = build_simple_func("eh_func");
builder.add_cie(&func, &EHPersonality::GCC_CXX);
builder.add_fde(&func, &[]);
let eh_frame = builder.emit_eh_frame();
assert!(!eh_frame.is_empty());
assert_eq!(builder.fde_count, 1);
}
#[test]
fn test_eh_frame_builder_multiple_fdes() {
let mut builder = EHFrameBuilder::new();
let func1 = build_simple_func("f1");
let func2 = build_simple_func("f2");
builder.add_cie(&func1, &EHPersonality::GCC_CXX);
builder.add_fde(&func1, &[]);
builder.add_fde(&func2, &[]);
assert_eq!(builder.fde_count, 2);
let eh_frame = builder.emit_eh_frame();
assert!(!eh_frame.is_empty());
}
}