From fec86c8352d57fd7764d0fa2c4975010c7de793e Mon Sep 17 00:00:00 2001 From: Levente Kurusa Date: Tue, 25 Sep 2018 20:35:19 +0200 Subject: [PATCH 1/5] codegen_llvm: check inline assembly constraints with LLVM LLVM provides a way of checking whether the constraints and the actual inline assembly make sense. This commit introduces a check before emitting code for the inline assembly. If LLVM rejects the inline assembly (or its constraints), then the compiler emits an error E0668 ("malformed inline assembly"). Signed-off-by: Levente Kurusa --- src/librustc_codegen_llvm/asm.rs | 8 +++++++- src/librustc_codegen_llvm/builder.rs | 16 ++++++++++++---- src/librustc_codegen_llvm/diagnostics.rs | 22 ++++++++++++++++++++++ src/librustc_codegen_llvm/llvm/ffi.rs | 3 +++ src/librustc_codegen_llvm/mir/statement.rs | 5 ++++- src/rustllvm/RustWrapper.cpp | 5 +++++ 6 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/librustc_codegen_llvm/asm.rs b/src/librustc_codegen_llvm/asm.rs index 5d27f8eab3ece..f1bb41bcebacf 100644 --- a/src/librustc_codegen_llvm/asm.rs +++ b/src/librustc_codegen_llvm/asm.rs @@ -30,7 +30,7 @@ pub fn codegen_inline_asm( ia: &hir::InlineAsm, outputs: Vec>, mut inputs: Vec<&'ll Value> -) { +) -> bool { let mut ext_constraints = vec![]; let mut output_types = vec![]; @@ -97,6 +97,10 @@ pub fn codegen_inline_asm( ia.alignstack, dialect ); + if r.is_none() { + return false; + } + let r = r.unwrap(); // Again, based on how many outputs we have let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); @@ -117,6 +121,8 @@ pub fn codegen_inline_asm( llvm::LLVMSetMetadata(r, kind, llvm::LLVMMDNodeInContext(bx.cx.llcx, &val, 1)); } + + return true; } pub fn codegen_global_asm<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index e3526a5a2eead..afb87e2723f8a 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -737,7 +737,7 @@ impl Builder<'a, 'll, 'tcx> { pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char, inputs: &[&'ll Value], output: &'ll Type, volatile: bool, alignstack: bool, - dia: AsmDialect) -> &'ll Value { + dia: AsmDialect) -> Option<&'ll Value> { self.count_insn("inlineasm"); let volatile = if volatile { llvm::True } @@ -753,9 +753,17 @@ impl Builder<'a, 'll, 'tcx> { debug!("Asm Output Type: {:?}", output); let fty = Type::func(&argtys[..], output); unsafe { - let v = llvm::LLVMRustInlineAsm( - fty, asm, cons, volatile, alignstack, dia); - self.call(v, inputs, None) + // Ask LLVM to verify that the constraints are well-formed. + let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons); + debug!("Constraint verification result: {:?}", constraints_ok); + if constraints_ok == 1 { + let v = llvm::LLVMRustInlineAsm( + fty, asm, cons, volatile, alignstack, dia); + Some(self.call(v, inputs, None)) + } else { + // LLVM has detected an issue with our constaints, bail out + None + } } } diff --git a/src/librustc_codegen_llvm/diagnostics.rs b/src/librustc_codegen_llvm/diagnostics.rs index 94776f17c7989..05359f62ac03c 100644 --- a/src/librustc_codegen_llvm/diagnostics.rs +++ b/src/librustc_codegen_llvm/diagnostics.rs @@ -47,4 +47,26 @@ unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok! ``` "##, +E0668: r##" +Malformed inline assembly rejected by LLVM. + +LLVM checks the validity of the constraints and the assembly string passed to +it. This error implies that LLVM seems something wrong with the inline +assembly call. + +In particular, it can happen if you forgot the closing bracket of a register +constraint (see issue #51430): +``` +#![feature(asm)] + +fn main() { + let rax: u64; + unsafe { + asm!("" :"={rax"(rax)); + println!("Accumulator is: {}", rax); + } +} +``` +"##, + } diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index a5f4137c62b14..7975e7ad67033 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -1208,6 +1208,9 @@ extern "C" { AlignStack: Bool, Dialect: AsmDialect) -> &Value; + pub fn LLVMRustInlineAsmVerify(Ty: &Type, + Constraints: *const c_char) + -> Bool; pub fn LLVMRustDebugMetadataVersion() -> u32; pub fn LLVMRustVersionMajor() -> u32; diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs index b4eb7615f98b4..eb7eb1dbd468a 100644 --- a/src/librustc_codegen_llvm/mir/statement.rs +++ b/src/librustc_codegen_llvm/mir/statement.rs @@ -86,7 +86,10 @@ impl FunctionCx<'a, 'll, 'tcx> { self.codegen_operand(&bx, input).immediate() }).collect(); - asm::codegen_inline_asm(&bx, asm, outputs, input_vals); + let res = asm::codegen_inline_asm(&bx, asm, outputs, input_vals); + if !res { + span_err!(bx.sess(), statement.source_info.span, E0668, "malformed inline assembly"); + } bx } mir::StatementKind::FakeRead(..) | diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 9b9c908ea5272..f1ab1d4ddfa47 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -426,6 +426,11 @@ extern "C" LLVMValueRef LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, HasSideEffects, IsAlignStack, fromRust(Dialect))); } +extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, + char *Constraints) { + return InlineAsm::Verify(unwrap(Ty), Constraints); +} + extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm) { unwrap(M)->appendModuleInlineAsm(StringRef(Asm)); } From 70bf90384c35056c620ad221982d346b3b92ee0a Mon Sep 17 00:00:00 2001 From: Levente Kurusa Date: Wed, 26 Sep 2018 06:43:19 +0200 Subject: [PATCH 2/5] fixup! codegen_llvm: check inline assembly constraints with LLVM --- src/librustc_codegen_llvm/builder.rs | 2 +- src/librustc_codegen_llvm/mir/statement.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index afb87e2723f8a..77de88997e490 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -756,7 +756,7 @@ impl Builder<'a, 'll, 'tcx> { // Ask LLVM to verify that the constraints are well-formed. let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons); debug!("Constraint verification result: {:?}", constraints_ok); - if constraints_ok == 1 { + if constraints_ok == llvm::True { let v = llvm::LLVMRustInlineAsm( fty, asm, cons, volatile, alignstack, dia); Some(self.call(v, inputs, None)) diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs index eb7eb1dbd468a..6bd41bfe16fee 100644 --- a/src/librustc_codegen_llvm/mir/statement.rs +++ b/src/librustc_codegen_llvm/mir/statement.rs @@ -88,7 +88,8 @@ impl FunctionCx<'a, 'll, 'tcx> { let res = asm::codegen_inline_asm(&bx, asm, outputs, input_vals); if !res { - span_err!(bx.sess(), statement.source_info.span, E0668, "malformed inline assembly"); + span_err!(bx.sess(), statement.source_info.span, E0668, + "malformed inline assembly"); } bx } From 687fdf5663f24f155f35dea5670fa9d5bc8ff738 Mon Sep 17 00:00:00 2001 From: Levente Kurusa Date: Wed, 26 Sep 2018 08:44:19 +0200 Subject: [PATCH 3/5] fixup! codegen_llvm: check inline assembly constraints with LLVM --- src/librustc_codegen_llvm/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_codegen_llvm/diagnostics.rs b/src/librustc_codegen_llvm/diagnostics.rs index 05359f62ac03c..668d602a69c2d 100644 --- a/src/librustc_codegen_llvm/diagnostics.rs +++ b/src/librustc_codegen_llvm/diagnostics.rs @@ -56,7 +56,7 @@ assembly call. In particular, it can happen if you forgot the closing bracket of a register constraint (see issue #51430): -``` +```compile_fail #![feature(asm)] fn main() { From ecd51ed90b1a913d7d47138b51dad64946737958 Mon Sep 17 00:00:00 2001 From: Levente Kurusa Date: Wed, 26 Sep 2018 10:19:09 +0200 Subject: [PATCH 4/5] fixup! codegen_llvm: check inline assembly constraints with LLVM --- src/librustc_codegen_llvm/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_codegen_llvm/diagnostics.rs b/src/librustc_codegen_llvm/diagnostics.rs index 668d602a69c2d..242b7a1a119f7 100644 --- a/src/librustc_codegen_llvm/diagnostics.rs +++ b/src/librustc_codegen_llvm/diagnostics.rs @@ -56,7 +56,7 @@ assembly call. In particular, it can happen if you forgot the closing bracket of a register constraint (see issue #51430): -```compile_fail +```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail) #![feature(asm)] fn main() { From 0991d2098f9b750331962ad5566200644a00f69e Mon Sep 17 00:00:00 2001 From: Levente Kurusa Date: Wed, 26 Sep 2018 16:23:10 +0200 Subject: [PATCH 5/5] fixup! codegen_llvm: check inline assembly constraints with LLVM --- src/test/ui/inline-asm-bad-constraint.rs | 47 ++++++++++++++++++++ src/test/ui/inline-asm-bad-constraint.stderr | 21 +++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/test/ui/inline-asm-bad-constraint.rs create mode 100644 src/test/ui/inline-asm-bad-constraint.stderr diff --git a/src/test/ui/inline-asm-bad-constraint.rs b/src/test/ui/inline-asm-bad-constraint.rs new file mode 100644 index 0000000000000..654f230741e96 --- /dev/null +++ b/src/test/ui/inline-asm-bad-constraint.rs @@ -0,0 +1,47 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that the compiler will catch invalid inline assembly constraints. + +#![feature(asm)] + +extern "C" { + fn foo(a: usize); +} + +fn main() { + bad_register_constraint(); + bad_input(); + wrong_size_output(); +} + +// Issue #54130 +fn bad_register_constraint() { + let rax: u64; + unsafe { + asm!("" :"={rax"(rax)) //~ ERROR E0668 + }; + println!("Accumulator is: {}", rax); +} + +// Issue #54376 +fn bad_input() { + unsafe { + asm!("callq $0" : : "0"(foo)) //~ ERROR E0668 + }; +} + +fn wrong_size_output() { + let rax: u64 = 0; + unsafe { + asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668 + } + println!("rax: {}", rax); +} diff --git a/src/test/ui/inline-asm-bad-constraint.stderr b/src/test/ui/inline-asm-bad-constraint.stderr new file mode 100644 index 0000000000000..ce1f274749f1f --- /dev/null +++ b/src/test/ui/inline-asm-bad-constraint.stderr @@ -0,0 +1,21 @@ +error[E0668]: malformed inline assembly + --> $DIR/inline-asm-bad-constraint.rs:29:9 + | +LL | asm!("" :"={rax"(rax)) //~ ERROR E0668 + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0668]: malformed inline assembly + --> $DIR/inline-asm-bad-constraint.rs:37:9 + | +LL | asm!("callq $0" : : "0"(foo)) //~ ERROR E0668 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0668]: malformed inline assembly + --> $DIR/inline-asm-bad-constraint.rs:44:9 + | +LL | asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0668`.