Browse Source

Special case Rust unwind terminate for forced unwind

This changes the personality to match the current libstd impl.
Gary Guo 7 months ago
parent
commit
2f19304851
2 changed files with 30 additions and 13 deletions
  1. 1 1
      Cargo.lock
  2. 29 12
      src/personality.rs

+ 1 - 1
Cargo.lock

@@ -69,7 +69,7 @@ dependencies = [
 
 [[package]]
 name = "unwinding"
-version = "0.2.1"
+version = "0.2.2"
 dependencies = [
  "compiler_builtins",
  "gimli",

+ 29 - 12
src/personality.rs

@@ -16,6 +16,8 @@ enum EHAction {
     None,
     Cleanup(usize),
     Catch(usize),
+    Filter(usize),
+    Terminate,
 }
 
 fn parse_pointer_encoding(input: &mut StaticSlice) -> gimli::Result<constants::DwEhPe> {
@@ -92,31 +94,31 @@ fn find_eh_action(
 
     let call_site_encoding = parse_pointer_encoding(reader)?;
     let call_site_table_length = reader.read_uleb128()?;
-    reader.truncate(call_site_table_length as _)?;
+    let (mut call_site_table, mut action_table) = reader.split_at(call_site_table_length as _);
 
-    while !reader.is_empty() {
+    while !call_site_table.is_empty() {
         let cs_start = unsafe {
             deref_pointer(parse_encoded_pointer(
                 call_site_encoding,
                 unwind_ctx,
-                reader,
+                &mut call_site_table,
             )?)
         };
         let cs_len = unsafe {
             deref_pointer(parse_encoded_pointer(
                 call_site_encoding,
                 unwind_ctx,
-                reader,
+                &mut call_site_table,
             )?)
         };
         let cs_lpad = unsafe {
             deref_pointer(parse_encoded_pointer(
                 call_site_encoding,
                 unwind_ctx,
-                reader,
+                &mut call_site_table,
             )?)
         };
-        let cs_action = reader.read_uleb128()?;
+        let cs_action = call_site_table.read_uleb128()?;
         if ip < func_start + cs_start {
             break;
         }
@@ -125,14 +127,23 @@ fn find_eh_action(
                 return Ok(EHAction::None);
             } else {
                 let lpad = lpad_base + cs_lpad;
-                return Ok(match cs_action {
-                    0 => EHAction::Cleanup(lpad),
-                    _ => EHAction::Catch(lpad),
+                if cs_action == 0 {
+                    return Ok(EHAction::Cleanup(lpad));
+                }
+
+                action_table.skip((cs_action - 1) as _)?;
+                let ttype_index = action_table.read_sleb128()?;
+                return Ok(if ttype_index == 0 {
+                    EHAction::Cleanup(lpad)
+                } else if ttype_index > 0 {
+                    EHAction::Catch(lpad)
+                } else {
+                    EHAction::Filter(lpad)
                 });
             }
         }
     }
-    Ok(EHAction::None)
+    Ok(EHAction::Terminate)
 }
 
 #[lang = "eh_personality"]
@@ -161,12 +172,17 @@ unsafe fn rust_eh_personality(
     if actions.contains(UnwindAction::SEARCH_PHASE) {
         match eh_action {
             EHAction::None | EHAction::Cleanup(_) => UnwindReasonCode::CONTINUE_UNWIND,
-            EHAction::Catch(_) => UnwindReasonCode::HANDLER_FOUND,
+            EHAction::Catch(_) | EHAction::Filter(_) => UnwindReasonCode::HANDLER_FOUND,
+            EHAction::Terminate => UnwindReasonCode::FATAL_PHASE1_ERROR,
         }
     } else {
         match eh_action {
             EHAction::None => UnwindReasonCode::CONTINUE_UNWIND,
-            EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => {
+            // Forced unwinding hits a terminate action.
+            EHAction::Filter(_) if actions.contains(UnwindAction::FORCE_UNWIND) => {
+                UnwindReasonCode::CONTINUE_UNWIND
+            }
+            EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => {
                 _Unwind_SetGR(
                     unwind_ctx,
                     Arch::UNWIND_DATA_REG.0 .0 as _,
@@ -176,6 +192,7 @@ unsafe fn rust_eh_personality(
                 _Unwind_SetIP(unwind_ctx, lpad);
                 UnwindReasonCode::INSTALL_CONTEXT
             }
+            EHAction::Terminate => UnwindReasonCode::FATAL_PHASE2_ERROR,
         }
     }
 }