personality.rs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // References:
  2. // https://github.com/rust-lang/rust/blob/c4be230b4a30eb74e3a3908455731ebc2f731d3d/library/panic_unwind/src/gcc.rs
  3. // https://github.com/rust-lang/rust/blob/c4be230b4a30eb74e3a3908455731ebc2f731d3d/library/panic_unwind/src/dwarf/eh.rs
  4. // https://docs.rs/gimli/0.25.0/src/gimli/read/cfi.rs.html
  5. use core::mem;
  6. use gimli::{constants, NativeEndian};
  7. use gimli::{EndianSlice, Error, Pointer, Reader};
  8. use crate::abi::*;
  9. use crate::arch::*;
  10. use crate::util::*;
  11. #[derive(Debug)]
  12. enum EHAction {
  13. None,
  14. Cleanup(usize),
  15. Catch(usize),
  16. }
  17. fn parse_pointer_encoding(input: &mut StaticSlice) -> gimli::Result<constants::DwEhPe> {
  18. let eh_pe = input.read_u8()?;
  19. let eh_pe = constants::DwEhPe(eh_pe);
  20. if eh_pe.is_valid_encoding() {
  21. Ok(eh_pe)
  22. } else {
  23. Err(gimli::Error::UnknownPointerEncoding)
  24. }
  25. }
  26. fn parse_encoded_pointer(
  27. encoding: constants::DwEhPe,
  28. unwind_ctx: &UnwindContext<'_>,
  29. input: &mut StaticSlice,
  30. ) -> gimli::Result<Pointer> {
  31. if encoding == constants::DW_EH_PE_omit {
  32. return Err(Error::CannotParseOmitPointerEncoding);
  33. }
  34. let base = match encoding.application() {
  35. constants::DW_EH_PE_absptr => 0,
  36. constants::DW_EH_PE_pcrel => input.slice().as_ptr() as u64,
  37. constants::DW_EH_PE_textrel => _Unwind_GetTextRelBase(unwind_ctx) as u64,
  38. constants::DW_EH_PE_datarel => _Unwind_GetDataRelBase(unwind_ctx) as u64,
  39. constants::DW_EH_PE_funcrel => _Unwind_GetRegionStart(unwind_ctx) as u64,
  40. constants::DW_EH_PE_aligned => return Err(Error::UnsupportedPointerEncoding),
  41. _ => unreachable!(),
  42. };
  43. let offset = match encoding.format() {
  44. constants::DW_EH_PE_absptr => input.read_address(mem::size_of::<usize>() as _),
  45. constants::DW_EH_PE_uleb128 => input.read_uleb128(),
  46. constants::DW_EH_PE_udata2 => input.read_u16().map(u64::from),
  47. constants::DW_EH_PE_udata4 => input.read_u32().map(u64::from),
  48. constants::DW_EH_PE_udata8 => input.read_u64(),
  49. constants::DW_EH_PE_sleb128 => input.read_sleb128().map(|a| a as u64),
  50. constants::DW_EH_PE_sdata2 => input.read_i16().map(|a| a as u64),
  51. constants::DW_EH_PE_sdata4 => input.read_i32().map(|a| a as u64),
  52. constants::DW_EH_PE_sdata8 => input.read_i64().map(|a| a as u64),
  53. _ => unreachable!(),
  54. }?;
  55. let address = base.wrapping_add(offset);
  56. Ok(if encoding.is_indirect() {
  57. Pointer::Indirect(address)
  58. } else {
  59. Pointer::Direct(address)
  60. })
  61. }
  62. fn find_eh_action(
  63. reader: &mut StaticSlice,
  64. unwind_ctx: &UnwindContext<'_>,
  65. ) -> gimli::Result<EHAction> {
  66. let func_start = _Unwind_GetRegionStart(unwind_ctx);
  67. let mut ip_before_instr = 0;
  68. let ip = _Unwind_GetIPInfo(unwind_ctx, &mut ip_before_instr);
  69. let ip = if ip_before_instr != 0 { ip } else { ip - 1 };
  70. let start_encoding = parse_pointer_encoding(reader)?;
  71. let lpad_base = if !start_encoding.is_absent() {
  72. unsafe { deref_pointer(parse_encoded_pointer(start_encoding, unwind_ctx, reader)?) }
  73. } else {
  74. func_start
  75. };
  76. let ttype_encoding = parse_pointer_encoding(reader)?;
  77. if !ttype_encoding.is_absent() {
  78. reader.read_uleb128()?;
  79. }
  80. let call_site_encoding = parse_pointer_encoding(reader)?;
  81. let call_site_table_length = reader.read_uleb128()?;
  82. reader.truncate(call_site_table_length as _)?;
  83. while !reader.is_empty() {
  84. let cs_start = unsafe {
  85. deref_pointer(parse_encoded_pointer(
  86. call_site_encoding,
  87. unwind_ctx,
  88. reader,
  89. )?)
  90. };
  91. let cs_len = unsafe {
  92. deref_pointer(parse_encoded_pointer(
  93. call_site_encoding,
  94. unwind_ctx,
  95. reader,
  96. )?)
  97. };
  98. let cs_lpad = unsafe {
  99. deref_pointer(parse_encoded_pointer(
  100. call_site_encoding,
  101. unwind_ctx,
  102. reader,
  103. )?)
  104. };
  105. let cs_action = reader.read_uleb128()?;
  106. if ip < func_start + cs_start {
  107. break;
  108. }
  109. if ip < func_start + cs_start + cs_len {
  110. if cs_lpad == 0 {
  111. return Ok(EHAction::None);
  112. } else {
  113. let lpad = lpad_base + cs_lpad;
  114. return Ok(match cs_action {
  115. 0 => EHAction::Cleanup(lpad),
  116. _ => EHAction::Catch(lpad),
  117. });
  118. }
  119. }
  120. }
  121. Ok(EHAction::None)
  122. }
  123. #[lang = "eh_personality"]
  124. fn rust_eh_personality(
  125. version: c_int,
  126. actions: UnwindAction,
  127. _exception_class: u64,
  128. exception: &mut UnwindException,
  129. unwind_ctx: &mut UnwindContext<'_>,
  130. ) -> UnwindReasonCode {
  131. if version != 1 {
  132. return UnwindReasonCode::FATAL_PHASE1_ERROR;
  133. }
  134. let lsda = _Unwind_GetLanguageSpecificData(unwind_ctx);
  135. if lsda.is_null() {
  136. return UnwindReasonCode::CONTINUE_UNWIND;
  137. }
  138. let mut lsda = EndianSlice::new(unsafe { get_unlimited_slice(lsda as _) }, NativeEndian);
  139. let eh_action = match find_eh_action(&mut lsda, unwind_ctx) {
  140. Ok(v) => v,
  141. Err(_) => return UnwindReasonCode::FATAL_PHASE1_ERROR,
  142. };
  143. if actions.contains(UnwindAction::SEARCH_PHASE) {
  144. match eh_action {
  145. EHAction::None | EHAction::Cleanup(_) => UnwindReasonCode::CONTINUE_UNWIND,
  146. EHAction::Catch(_) => UnwindReasonCode::HANDLER_FOUND,
  147. }
  148. } else {
  149. match eh_action {
  150. EHAction::None => UnwindReasonCode::CONTINUE_UNWIND,
  151. EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => {
  152. _Unwind_SetGR(
  153. unwind_ctx,
  154. Arch::UNWIND_DATA_REG.0 .0 as _,
  155. exception as *mut _ as usize,
  156. );
  157. _Unwind_SetGR(unwind_ctx, Arch::UNWIND_DATA_REG.1 .0 as _, 0);
  158. _Unwind_SetIP(unwind_ctx, lpad);
  159. UnwindReasonCode::INSTALL_CONTEXT
  160. }
  161. }
  162. }
  163. }