diff --git a/cranelift/docs/langref.rst b/cranelift/docs/langref.rst index c8c7ede4fd..1e148190e6 100644 --- a/cranelift/docs/langref.rst +++ b/cranelift/docs/langref.rst @@ -411,13 +411,7 @@ This simple example illustrates direct function calls and signatures:: Indirect function calls use a signature declared in the preamble. .. autoinst:: call_indirect - -.. todo:: Define safe indirect function calls. - - The :inst:`call_indirect` instruction is dangerous to use in a sandboxed - environment since it is not easy to verify the callee address. - We need a table-driven indirect call instruction, similar to - :inst:`br_table`. +.. autoinst:: func_addr Memory diff --git a/cranelift/filetests/parser/call.cton b/cranelift/filetests/parser/call.cton index 662925db7f..f4ed00c222 100644 --- a/cranelift/filetests/parser/call.cton +++ b/cranelift/filetests/parser/call.cton @@ -69,6 +69,19 @@ ebb0(v0: i64): ; check: $v3, $v4 = call_indirect $sig2, $v1() ; check: return +function %long_call() { + sig0 = () + fn0 = sig0 %none + +ebb0: + v0 = func_addr.i32 fn0 + call_indirect sig0, v0() + return +} +; check: $v0 = func_addr.i32 $fn0 +; check: call_indirect $sig0, $v0() +; check: return + ; Special purpose function arguments function %special1(i32 sret, i32 fp, i32 csr, i32 link) -> i32 link, i32 fp, i32 csr, i32 sret { ebb0(v1: i32, v2: i32, v3: i32, v4: i32): diff --git a/lib/cretonne/meta/base/formats.py b/lib/cretonne/meta/base/formats.py index 64a9adb456..783539e105 100644 --- a/lib/cretonne/meta/base/formats.py +++ b/lib/cretonne/meta/base/formats.py @@ -49,6 +49,7 @@ BranchTable = InstructionFormat(VALUE, entities.jump_table) Call = InstructionFormat(func_ref, VARIABLE_ARGS) IndirectCall = InstructionFormat(sig_ref, VALUE, VARIABLE_ARGS) +FuncAddr = InstructionFormat(func_ref) Load = InstructionFormat(memflags, VALUE, offset32) Store = InstructionFormat(memflags, VALUE, VALUE, offset32) diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index 482cb2a12d..735ec742bc 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/lib/cretonne/meta/base/instructions.py @@ -38,6 +38,8 @@ MemTo = TypeVar( 'MemTo', 'Any type that can be stored in memory', ints=True, floats=True, simd=True) +addr = Operand('addr', iAddr) + # # Control flow # @@ -185,6 +187,18 @@ call_indirect = Instruction( """, ins=(SIG, callee, args), outs=rvals, is_call=True) +func_addr = Instruction( + 'func_addr', r""" + Get the address of a function. + + Compute the absolute address of a function declared in the preamble. + The returned address can be used as a ``callee`` argument to + :inst:`call_indirect`. This is also a method for calling functions that + are too far away to be addressable by a direct :inst:`call` + instruction. + """, + ins=FN, outs=addr) + # # Memory operations # @@ -194,7 +208,6 @@ Offset = Operand('Offset', offset32, 'Byte offset from base address') x = Operand('x', Mem, doc='Value to be stored') a = Operand('a', Mem, doc='Value loaded') p = Operand('p', iAddr) -addr = Operand('addr', iAddr) Flags = Operand('Flags', memflags) load = Instruction( diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index b37646afbf..9a9301e7db 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -178,6 +178,7 @@ pub enum InstructionData { sig_ref: SigRef, args: ValueList, }, + FuncAddr { opcode: Opcode, func_ref: FuncRef }, StackLoad { opcode: Opcode, stack_slot: StackSlot, diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index cded1ffc70..7fcb4b508f 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -306,6 +306,9 @@ impl<'a> Verifier<'a> { self.verify_sig_ref(inst, sig_ref)?; self.verify_value_list(inst, args)?; } + FuncAddr { func_ref, .. } => { + self.verify_func_ref(inst, func_ref)?; + } StackLoad { stack_slot, .. } | StackStore { stack_slot, .. } => { self.verify_stack_slot(inst, stack_slot)?; diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index 3467f79adb..56d1eb4652 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -346,6 +346,7 @@ pub fn write_operands( DisplayValues(&args[1..]) ) } + FuncAddr { func_ref, .. } => write!(w, " {}", func_ref), StackLoad { stack_slot, offset, .. } => write!(w, " {}{}", stack_slot, offset), StackStore { arg, diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index f9b3f334e1..d9dd334964 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -2098,6 +2098,14 @@ impl<'a> Parser<'a> { args: args.into_value_list(&[callee], &mut ctx.function.dfg.value_lists), } } + InstructionFormat::FuncAddr => { + let func_ref = self.match_fn("expected function reference").and_then( + |num| { + ctx.get_fn(num, &self.loc) + }, + )?; + InstructionData::FuncAddr { opcode, func_ref } + } InstructionFormat::BranchTable => { let arg = self.match_value("expected SSA value operand")?; self.match_token(