about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Henningsson <diwic@ubuntu.com>2017-12-19 01:59:29 +0100
committerDavid Henningsson <diwic@ubuntu.com>2017-12-21 04:43:35 +0100
commitf536143ab68c9db352e78269b3ec72b358b18548 (patch)
treeeb0bfe6e281008e676e8aa364831abeacc942b29
parentdd6127e4af0a0b899c82944b023bf3e7e2949175 (diff)
downloadrust-f536143ab68c9db352e78269b3ec72b358b18548.tar.gz
rust-f536143ab68c9db352e78269b3ec72b358b18548.zip
Mir: Abort on nounwind ABIs
Generate Abort instead of Resume terminators on nounwind ABIs.

https://github.com/rust-lang/rust/issues/18510

Signed-off-by: David Henningsson <diwic@ubuntu.com>
-rw-r--r--src/Cargo.lock1
-rw-r--r--src/librustc_mir/Cargo.toml1
-rw-r--r--src/librustc_mir/build/mod.rs27
-rw-r--r--src/librustc_mir/build/scope.rs10
-rw-r--r--src/librustc_mir/lib.rs1
-rw-r--r--src/test/run-pass/abort-on-c-abi.rs43
6 files changed, 83 insertions, 0 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock
index c22187ee13e..f904bd5bf43 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -1919,6 +1919,7 @@ dependencies = [
  "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc 0.0.0",
  "rustc_apfloat 0.0.0",
+ "rustc_back 0.0.0",
  "rustc_const_eval 0.0.0",
  "rustc_const_math 0.0.0",
  "rustc_data_structures 0.0.0",
diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml
index a54f15532b1..ad64244f7c5 100644
--- a/src/librustc_mir/Cargo.toml
+++ b/src/librustc_mir/Cargo.toml
@@ -14,6 +14,7 @@ graphviz = { path = "../libgraphviz" }
 log = "0.3"
 log_settings = "0.1.1"
 rustc = { path = "../librustc" }
+rustc_back = { path = "../librustc_back" }
 rustc_const_eval = { path = "../librustc_const_eval" }
 rustc_const_math = { path = "../librustc_const_math" }
 rustc_data_structures = { path = "../librustc_data_structures" }
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index d814b092c9d..4b8ec062a78 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -20,6 +20,7 @@ use rustc::mir::visit::{MutVisitor, TyContext};
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::subst::Substs;
 use rustc::util::nodemap::NodeMap;
+use rustc_back::PanicStrategy;
 use rustc_const_eval::pattern::{BindingMode, PatternKind};
 use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 use shim;
@@ -353,6 +354,27 @@ macro_rules! unpack {
     };
 }
 
+fn needs_abort_block<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                     fn_id: ast::NodeId,
+                                     abi: Abi)
+                                     -> bool {
+
+    // Not callable from C, so we can safely unwind through these
+    if abi == Abi::Rust || abi == Abi::RustCall { return false; }
+
+    // We never unwind, so it's not relevant to stop an unwind
+    if tcx.sess.panic_strategy() != PanicStrategy::Unwind { return false; }
+
+    // We cannot add landing pads, so don't add one
+    if tcx.sess.no_landing_pads() { return false; }
+
+    // This is a special case: some functions have a C abi but are meant to
+    // unwind anyway. Don't stop them.
+    if tcx.has_attr(tcx.hir.local_def_id(fn_id), "unwind") { return false; }
+
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////
 /// the main entry point for building MIR for a function
 
@@ -383,6 +405,11 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
     let source_info = builder.source_info(span);
     let call_site_s = (call_site_scope, source_info);
     unpack!(block = builder.in_scope(call_site_s, LintLevel::Inherited, block, |builder| {
+
+        if needs_abort_block(tcx, fn_id, abi) {
+            builder.schedule_abort();
+        }
+
         let arg_scope_s = (arg_scope, source_info);
         unpack!(block = builder.in_scope(arg_scope_s, LintLevel::Inherited, block, |builder| {
             builder.args_and_body(block, &arguments, arg_scope, &body.value)
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index 630d0bf1792..ddb4bf0e36b 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -612,6 +612,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         }
     }
 
+    // Schedule an abort block - this is used for some ABIs that cannot unwind
+    pub fn schedule_abort(&mut self) -> BasicBlock {
+        self.scopes[0].needs_cleanup = true;
+        let abortblk = self.cfg.start_new_cleanup_block();
+        let source_info = self.scopes[0].source_info(self.fn_span);
+        self.cfg.terminate(abortblk, source_info, TerminatorKind::Abort);
+        self.cached_resume_block = Some(abortblk);
+        abortblk
+    }
+
     // Scheduling drops
     // ================
     /// Indicates that `place` should be dropped on exit from
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index a7efdaf0826..4556a8ecc4b 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -49,6 +49,7 @@ extern crate rustc_errors;
 #[macro_use]
 extern crate syntax;
 extern crate syntax_pos;
+extern crate rustc_back;
 extern crate rustc_const_math;
 extern crate rustc_const_eval;
 extern crate core; // for NonZero
diff --git a/src/test/run-pass/abort-on-c-abi.rs b/src/test/run-pass/abort-on-c-abi.rs
new file mode 100644
index 00000000000..63fd934b0d0
--- /dev/null
+++ b/src/test/run-pass/abort-on-c-abi.rs
@@ -0,0 +1,43 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Since we mark some ABIs as "nounwind" to LLVM, we must make sure that
+// we never unwind through them.
+
+// ignore-emscripten no processes
+
+use std::{env, panic};
+use std::io::prelude::*;
+use std::io;
+use std::process::{Command, Stdio};
+
+extern "C" fn panic_in_ffi() {
+    panic!("Test");
+}
+
+fn test() {
+    let _ = panic::catch_unwind(|| { panic_in_ffi(); });
+    // The process should have aborted by now.
+    io::stdout().write(b"This should never be printed.\n");
+    let _ = io::stdout().flush();
+}
+
+fn main() {
+    let args: Vec<String> = env::args().collect();
+    if args.len() > 1 && args[1] == "test" {
+        return test();
+    }
+
+    let mut p = Command::new(&args[0])
+                        .stdout(Stdio::piped())
+                        .stdin(Stdio::piped())
+                        .arg("test").spawn().unwrap();
+    assert!(!p.wait().unwrap().success());
+}