To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.7% of users enabled 2FA.

Commit 210ee90e authored by Kunshan Wang's avatar Kunshan Wang
Browse files

Calling native functions.

parent 25c803ec
...@@ -13,6 +13,8 @@ libraryDependencies := Seq( ...@@ -13,6 +13,8 @@ libraryDependencies := Seq(
"com.typesafe.scala-logging" %% "scala-logging" % "3.1.0", "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0",
"ch.qos.logback" % "logback-classic" % "1.1.2", "ch.qos.logback" % "logback-classic" % "1.1.2",
"com.github.jnr" % "jnr-ffi" % "2.0.3", "com.github.jnr" % "jnr-ffi" % "2.0.3",
"com.github.jnr" % "jffi" % "1.2.9",
"com.github.jnr" % "jnr-posix" % "3.0.17",
"org.scalatest" %% "scalatest" % "2.2.0" "org.scalatest" %% "scalatest" % "2.2.0"
) )
......
...@@ -7,6 +7,7 @@ import uvm.refimpl.mem.TypeSizes.Word ...@@ -7,6 +7,7 @@ import uvm.refimpl.mem.TypeSizes.Word
import scala.collection.mutable.HashSet import scala.collection.mutable.HashSet
import uvm.ir.textinput.UIRTextReader import uvm.ir.textinput.UIRTextReader
import uvm.ir.textinput.IDFactory import uvm.ir.textinput.IDFactory
import uvm.refimpl.nat.NativeHelper
object MicroVM { object MicroVM {
val DEFAULT_HEAP_SIZE: Word = 4L * 1024L * 1024L; // 4MiB val DEFAULT_HEAP_SIZE: Word = 4L * 1024L * 1024L; // 4MiB
...@@ -26,6 +27,8 @@ class MicroVM(heapSize: Word = MicroVM.DEFAULT_HEAP_SIZE, ...@@ -26,6 +27,8 @@ class MicroVM(heapSize: Word = MicroVM.DEFAULT_HEAP_SIZE,
val memoryManager = new MemoryManager(heapSize, globalSize, stackSize) val memoryManager = new MemoryManager(heapSize, globalSize, stackSize)
private implicit val memorySupport = memoryManager.memorySupport private implicit val memorySupport = memoryManager.memorySupport
val nativeHelper = new NativeHelper()
val threadStackManager = new ThreadStackManager() val threadStackManager = new ThreadStackManager()
val trapManager = new TrapManager() val trapManager = new TrapManager()
......
...@@ -497,7 +497,7 @@ class ClientAgent(mutator: Mutator)( ...@@ -497,7 +497,7 @@ class ClientAgent(mutator: Mutator)(
} }
val box = newType match { val box = newType match {
case TypeInt(n) => new BoxInt(OpHelper.trunc(BigInt(addr), n)) case TypeInt(n) => new BoxInt(OpHelper.trunc(BigInt(addr), Math.min(n, 64)))
case _: AbstractPointerType => new BoxPointer(addr) case _: AbstractPointerType => new BoxPointer(addr)
} }
...@@ -505,13 +505,13 @@ class ClientAgent(mutator: Mutator)( ...@@ -505,13 +505,13 @@ class ClientAgent(mutator: Mutator)(
} }
def pin(handle: Handle): Handle = { def pin(handle: Handle): Handle = {
val (objTy, objRef) = handle.ty match { val (objTy, (objRef, offset)) = handle.ty match {
case TypeRef(t) => (t, handle.vb.asInstanceOf[BoxRef].objRef) case TypeRef(t) => (t, (handle.vb.asInstanceOf[BoxRef].objRef, 0L))
case TypeIRef(t) => (t, handle.vb.asInstanceOf[BoxIRef].objRef) case TypeIRef(t) => (t, handle.vb.asInstanceOf[BoxIRef].oo)
} }
pin(objRef) pin(objRef)
val ptrTy = InternalTypePool.ptrOf(objTy) val ptrTy = InternalTypePool.ptrOf(objTy)
val box = new BoxPointer(objRef) val box = new BoxPointer(objRef + offset)
newHandle(ptrTy, box) newHandle(ptrTy, box)
} }
......
...@@ -3,6 +3,7 @@ package uvm.refimpl ...@@ -3,6 +3,7 @@ package uvm.refimpl
import uvm._ import uvm._
import uvm.types._ import uvm.types._
import uvm.ssavariables._ import uvm.ssavariables._
import uvm.utils.LazyPool
import uvm.ir.textinput.IDFactory import uvm.ir.textinput.IDFactory
import scala.collection.mutable.HashMap import scala.collection.mutable.HashMap
import uvm.FuncSig import uvm.FuncSig
...@@ -40,20 +41,12 @@ object InternalTypes { ...@@ -40,20 +41,12 @@ object InternalTypes {
} }
object InternalTypePool { object InternalTypePool {
class LazyPool[FromT, ToT](factory: FromT => ToT) {
val pool = HashMap[FromT, ToT]()
def apply(obj: FromT): ToT = pool.get(obj).getOrElse(factory(obj))
}
object LazyPool {
def apply[FromT, ToT](factory: FromT => ToT): LazyPool[FromT, ToT] = new LazyPool[FromT, ToT](factory)
}
val refOf = LazyPool(TypeRef) val refOf = LazyPool(TypeRef)
val irefOf = LazyPool(TypeIRef) val irefOf = LazyPool(TypeIRef)
val ptrOf = LazyPool(TypePtr) val ptrOf = LazyPool(TypePtr)
val funcOf = LazyPool(TypeFunc) val funcOf = LazyPool(TypeFunc)
val funcPtrOf = LazyPool(TypeFuncPtr) val funcPtrOf = LazyPool(TypeFuncPtr)
val vecOf = new LazyPool[(Type, Long), TypeVector]({ case (t, l) => TypeVector(t, l) }) val vecOf = LazyPool[(Type, Long), TypeVector] { case (t, l) => TypeVector(t, l) }
def unmarkedOf(t: Type): Type = t match { def unmarkedOf(t: Type): Type = t match {
case TypeWeakRef(r) => refOf(r) case TypeWeakRef(r) => refOf(r)
case _ => t case _ => t
......
...@@ -373,20 +373,12 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator ...@@ -373,20 +373,12 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
case _ => case _ =>
} }
val srcAddr: Word = scalarFromTy match { val srcAddr: Word = scalarFromTy match {
case TypeInt(n) => { case TypeInt(n) => bOpnd.asInstanceOf[BoxInt].value.longValue // truncates
val od = bOpnd.asInstanceOf[BoxInt].value case _: AbstractPointerType => bOpnd.asInstanceOf[BoxPointer].addr
val truncExt = if (n >= 64) OpHelper.trunc(od, 64) else OpHelper.zext(od, n, 64)
truncExt.toLong
}
case TypePtr(_) | TypeFuncPtr(_) => bOpnd.asInstanceOf[BoxPointer].addr
} }
scalarToTy match { scalarToTy match {
case TypeInt(n) => { case TypeInt(n) => br.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(srcAddr), Math.min(n, 64))
val bi = BigInt(srcAddr) case _: AbstractPointerType => br.asInstanceOf[BoxPointer].addr = srcAddr
val truncExt = if (n > 64) OpHelper.zext(bi, 64, n) else OpHelper.trunc(bi, n)
br.asInstanceOf[BoxInt].value = truncExt
}
case TypePtr(_) | TypeFuncPtr(_) => br.asInstanceOf[BoxPointer].addr = srcAddr
} }
} }
...@@ -779,7 +771,18 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator ...@@ -779,7 +771,18 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
} }
case i @ InstCCall(callConv, funcTy, sig, callee, argList, keepAlives) => { case i @ InstCCall(callConv, funcTy, sig, callee, argList, keepAlives) => {
throw new UvmRefImplException(ctx + "The CCALL instruction is not implemented in this reference implementation") if (callConv != Flag("#DEFAULT")) {
throw new UvmRefImplException(ctx + "Currently only support the #DEFAULT callConv. %s found.".format(callConv.name))
}
val addr = boxOf(callee).asInstanceOf[BoxPointer].addr
val argBoxes = argList.map(boxOf)
val retBox = boxOf(i)
microVM.nativeHelper.callNative(sig, addr, argBoxes, retBox)
continueNormally()
} }
case i @ InstNewStack(sig, callee, argList, excClause) => { case i @ InstNewStack(sig, callee, argList, excClause) => {
...@@ -1064,29 +1067,29 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator ...@@ -1064,29 +1067,29 @@ class InterpreterThread(val id: Int, initialStack: InterpreterStack, val mutator
case "@uvm.native.pin" => { case "@uvm.native.pin" => {
val Seq(ty) = typeList val Seq(ty) = typeList
val Seq(r) = argList val Seq(r) = argList
val addr = ty match { val (addr, offset) = ty match {
case TypeRef(_) => boxOf(r).asInstanceOf[BoxRef].objRef case TypeRef(_) => (boxOf(r).asInstanceOf[BoxRef].objRef, 0L)
case TypeIRef(_) => boxOf(r).asInstanceOf[BoxIRef].objRef case TypeIRef(_) => boxOf(r).asInstanceOf[BoxIRef].oo
} }
pin(addr) pin(addr)
boxOf(i).asInstanceOf[BoxPointer].addr = addr boxOf(i).asInstanceOf[BoxPointer].addr = addr + offset
continueNormally() continueNormally()
} }
case "@uvm.native.unpin" => { case "@uvm.native.unpin" => {
val Seq(ty) = typeList val Seq(ty) = typeList
val Seq(r) = argList val Seq(r) = argList
val addr = ty match { val addr = ty match {
case TypeRef(_) => boxOf(r).asInstanceOf[BoxRef].objRef case TypeRef(_) => boxOf(r).asInstanceOf[BoxRef].objRef
case TypeIRef(_) => boxOf(r).asInstanceOf[BoxIRef].objRef case TypeIRef(_) => boxOf(r).asInstanceOf[BoxIRef].objRef
} }
unpin(addr) unpin(addr)
continueNormally() continueNormally()
} }
// Insert more CommInsts here. // Insert more CommInsts here.
......
package uvm.refimpl.nat
import com.kenai.jffi.{ Type => JType, Struct => JStruct, Function => JFunction, HeapInvocationBuffer, Invoker }
import uvm.FuncSig
import uvm.refimpl.UvmRefImplException
import uvm.refimpl.itpr.ValueBox
import uvm.refimpl.mem.TypeSizes.Word
import uvm.types._
import uvm.types.{ Type => MType }
import uvm.utils.LazyPool
import javax.vecmath.Tuple2d
import uvm.refimpl.itpr._
import java.nio.ByteBuffer
import uvm.refimpl.mem.TypeSizes
/**
* Helps calling native functions. Based on JFFI.
*/
class NativeHelper {
val jffiTypePool: LazyPool[MType, JType] = LazyPool {
case TypeVoid() => JType.VOID
case TypeInt(8) => JType.SINT8
case TypeInt(16) => JType.SINT16
case TypeInt(32) => JType.SINT32
case TypeInt(64) => JType.SINT64
case TypeFloat() => JType.FLOAT
case TypeDouble() => JType.DOUBLE
case TypeVector(_, _) => throw new UvmRefImplException("Vectors are not implemented in native calls.")
case TypeStruct(fields) => {
val fieldsNativeTypes: Seq[JType] = fields.map(jffiTypePool.apply)
val strType = JStruct.newStruct(fieldsNativeTypes: _*)
strType
}
case _: AbstractPointerType => JType.POINTER
case t => throw new UvmRefImplException("Type %s cannot be used in native calls.".format(t.repr))
}
val jffiFuncPool = LazyPool[(FuncSig, Word), JFunction] {
case (sig, funcAddr) => {
val jParamTypes = sig.paramTy.map(jffiTypePool.apply)
val jRetTy = jffiTypePool(sig.retTy)
new JFunction(funcAddr, jRetTy, jParamTypes: _*)
}
}
private def putArgToBuf(buf: ByteBuffer, off: Int, mty: MType, vb: ValueBox): Unit = {
mty match {
case TypeInt(8) => buf.put(off, vb.asInstanceOf[BoxInt].value.toByte)
case TypeInt(16) => buf.putShort(off, vb.asInstanceOf[BoxInt].value.toShort)
case TypeInt(32) => buf.putInt(off, vb.asInstanceOf[BoxInt].value.toInt)
case TypeInt(64) => buf.putLong(off, vb.asInstanceOf[BoxInt].value.toLong)
case TypeFloat() => buf.putFloat(off, vb.asInstanceOf[BoxFloat].value)
case TypeDouble() => buf.putDouble(off, vb.asInstanceOf[BoxDouble].value)
case s @ TypeStruct(flds) => {
val fldvbs = vb.asInstanceOf[BoxStruct].values
for (((fty, fvb), i) <- (flds zip fldvbs).zipWithIndex) {
val off2 = TypeSizes.fieldOffsetOf(s, i)
putArgToBuf(buf, off + off2.toInt, mty, vb)
}
}
case _: AbstractPointerType => buf.putLong(off, vb.asInstanceOf[BoxPointer].addr)
}
}
private def putArg(hib: HeapInvocationBuffer, mty: MType, vb: ValueBox): Unit = {
mty match {
case TypeInt(8) => hib.putByte(vb.asInstanceOf[BoxInt].value.toByte)
case TypeInt(16) => hib.putShort(vb.asInstanceOf[BoxInt].value.toShort)
case TypeInt(32) => hib.putInt(vb.asInstanceOf[BoxInt].value.toInt)
case TypeInt(64) => hib.putLong(vb.asInstanceOf[BoxInt].value.toLong)
case TypeFloat() => hib.putFloat(vb.asInstanceOf[BoxFloat].value)
case TypeDouble() => hib.putDouble(vb.asInstanceOf[BoxDouble].value)
case TypeStruct(flds) => {
val buf = ByteBuffer.allocate(TypeSizes.sizeOf(mty).toInt)
putArgToBuf(buf, 0, mty, vb)
hib.putStruct(buf.array(), buf.arrayOffset())
}
case _: AbstractPointerType => hib.putAddress(vb.asInstanceOf[BoxPointer].addr)
}
}
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox], retBox: ValueBox): Unit = {
val jFunc = jffiFuncPool((sig, func))
val hib = new HeapInvocationBuffer(jFunc)
for ((mty, vb) <- (sig.paramTy zip args)) {
putArg(hib, mty, vb)
}
val inv = Invoker.getInstance
sig.retTy match {
case TypeInt(8) => {
val rv = inv.invokeInt(jFunc, hib).toByte
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 8)
}
case TypeInt(16) => {
val rv = inv.invokeInt(jFunc, hib).toShort
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 16)
}
case TypeInt(32) => {
val rv = inv.invokeInt(jFunc, hib)
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 32)
}
case TypeInt(64) => {
val rv = inv.invokeLong(jFunc, hib)
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 64)
}
case TypeFloat() => {
val rv = inv.invokeFloat(jFunc, hib)
retBox.asInstanceOf[BoxFloat].value = rv
}
case TypeDouble() => {
val rv = inv.invokeDouble(jFunc, hib)
retBox.asInstanceOf[BoxDouble].value = rv
}
case TypeStruct(flds) => {
val rv = inv.invokeStruct(jFunc, hib)
val buf = ByteBuffer.wrap(rv)
getArgFromBuf(buf, 0, sig.retTy, retBox)
}
case _: AbstractPointerType => {
val rv = inv.invokeAddress(jFunc, hib)
retBox.asInstanceOf[BoxPointer].addr = rv
}
}
}
private def getArgFromBuf(buf: ByteBuffer, off: Int, mty: MType, vb: ValueBox): Unit = {
mty match {
case TypeInt(8) => vb.asInstanceOf[BoxInt].value = OpHelper.trunc(buf.get(off), 8)
case TypeInt(16) => vb.asInstanceOf[BoxInt].value = OpHelper.trunc(buf.getShort(off), 16)
case TypeInt(32) => vb.asInstanceOf[BoxInt].value = OpHelper.trunc(buf.getInt(off), 32)
case TypeInt(64) => vb.asInstanceOf[BoxInt].value = OpHelper.trunc(buf.getLong(off), 64)
case TypeFloat() => vb.asInstanceOf[BoxFloat].value = buf.getFloat(off)
case TypeDouble() => vb.asInstanceOf[BoxDouble].value = buf.getDouble(off)
case s @ TypeStruct(flds) => {
val fldvbs = vb.asInstanceOf[BoxStruct].values
for (((fty, fvb), i) <- (flds zip fldvbs).zipWithIndex) {
val off2 = TypeSizes.fieldOffsetOf(s, i)
getArgFromBuf(buf, off + off2.toInt, mty, vb)
}
}
case _: AbstractPointerType => vb.asInstanceOf[BoxPointer].addr = buf.getLong(off)
}
}
}
\ No newline at end of file
package uvm.utils
import scala.collection.mutable.HashMap
class LazyPool[FromT, ToT](factory: FromT => ToT) {
val pool = HashMap[FromT, ToT]()
def apply(obj: FromT): ToT = pool.get(obj).getOrElse(factory(obj))
}
object LazyPool {
def apply[FromT, ToT](factory: FromT => ToT): LazyPool[FromT, ToT] = new LazyPool[FromT, ToT](factory)
}
package uvm.refimpl.itpr
import org.scalatest._
import java.io.FileReader
import uvm._
import uvm.types._
import uvm.ssavariables._
import uvm.refimpl._
import uvm.refimpl.itpr._
import MemoryOrder._
import AtomicRMWOptr._
import uvm.refimpl.mem.TypeSizes.Word
import ch.qos.logback.classic.Level._
import uvm.refimpl.UvmBundleTesterBase
import com.kenai.jffi.Library
import jnr.posix.POSIXFactory
class UvmInterpreterNativeTests extends UvmBundleTesterBase {
setLogLevels(
ROOT_LOGGER_NAME -> INFO,
"uvm.refimpl.itpr" -> DEBUG)
preloadBundles("tests/uvm-refimpl-test/native-tests.uir")
"The CCALL instruction" should "call the getpid() function" in {
val ca = microVM.newClientAgent()
val lib = Library.getDefault()
val funcAddr = lib.getSymbolAddress("getpid")
val posix = POSIXFactory.getPOSIX
val actualPID = posix.getpid
println("actualPID = %d".format(actualPID))
val func = ca.putFunction("@getpidtest")
val a0 = ca.putInt("@i64", funcAddr)
testFunc(ca, func, Seq(a0)) { (ca, th, st, wp) =>
val Seq(fp, rv) = ca.dumpKeepalives(st, 0)
fp.vb.asPointer shouldEqual funcAddr
rv.vb.asSInt(32) shouldEqual actualPID
TrapRebindPassVoid(st)
}
ca.close()
}
"The CCALL instruction" should "call the write() function" in {
val ca = microVM.newClientAgent()
val lib = Library.getDefault()
val funcAddr = lib.getSymbolAddress("write")
val func = ca.putFunction("@writetest")
val a0 = ca.putInt("@i64", funcAddr)
testFunc(ca, func, Seq(a0)) { (ca, th, st, wp) =>
val Seq(fp, rv, buf, bufV0P) = ca.dumpKeepalives(st, 0)
fp.vb.asPointer shouldEqual funcAddr
rv.vb.asSInt(64) shouldEqual 6
TrapRebindPassVoid(st)
}
ca.close()
}
"The CCALL instruction" should "call the memcpy() function" in {
val ca = microVM.newClientAgent()
val lib = Library.getDefault()
val funcAddr = lib.getSymbolAddress("memcpy")
val func = ca.putFunction("@memcpytest")
val a0 = ca.putInt("@i64", funcAddr)
testFunc(ca, func, Seq(a0)) { (ca, th, st, wp) =>
val Seq(fp, rv, ob, b0, b1, b2, b3, b4, b5) = ca.dumpKeepalives(st, 0)
fp.vb.asPointer shouldEqual funcAddr
rv.vb.asPointer shouldEqual ob.vb.asPointer
b0.vb.asSInt(8) shouldEqual 'H'
b1.vb.asSInt(8) shouldEqual 'e'
b2.vb.asSInt(8) shouldEqual 'l'
b3.vb.asSInt(8) shouldEqual 'l'
b4.vb.asSInt(8) shouldEqual 'o'
b5.vb.asSInt(8) shouldEqual '\n'
TrapRebindPassVoid(st)
}
ca.close()
}
}
\ No newline at end of file
.typedef @i1 = int<1>
.typedef @i6 = int<6>
.typedef @i8 = int<8>
.typedef @i16 = int<16>
.typedef @i32 = int<32>
.typedef @i52 = int<52>
.typedef @i64 = int<64>
.typedef @float = float
.typedef @double = double
.typedef @void = void
.funcsig @noparamsnoret = @void ()
.typedef @funcdumb = func<@noparamsnoret>
.typedef @thread = thread
.typedef @stack = stack
.typedef @tagref64 = tagref64
.const @TRUE <@i64> = 1
.const @FALSE <@i64> = 0
.const @I32_0 <@i32> = 0
.const @I32_1 <@i32> = 1
.const @I32_2 <@i32> = 2
.const @I32_3 <@i32> = 3
.const @I32_4 <@i32> = 4
.const @I32_5 <@i32> = 5
.const @I32_6 <@i32> = 6
.const @I32_7 <@i32> = 7
.const @I64_0 <@i64> = 0
.const @I64_1 <@i64> = 1
.const @I64_2 <@i64> = 2
.const @I64_3 <@i64> = 3
.const @I64_4 <@i64> = 4
.const @I64_5 <@i64> = 5
.const @I64_6 <@i64> = 6
.const @I64_7 <@i64> = 7
.const @F_0 <@float> = 0.0f
.const @F_1 <@float> = 1.0f
.const @F_2 <@float> = 2.0f
.const @F_3 <@float> = 3.0f
.const @F_4 <@float> = 4.0f
.const @F_5 <@float> = 5.0f
.const @F_6 <@float> = 6.0f
.const @F_7 <@float> = 7.0f
.const @D_0 <@double> = 0.0d
.const @D_1 <@double> = 1.0d
.const @D_2 <@double> = 2.0d
.const @D_3 <@double> = 3.0d
.const @D_4 <@double> = 4.0d
.const @D_5 <@double> = 5.0d
.const @D_6 <@double> = 6.0d
.const @D_7 <@double> = 7.0d
.typedef @4xfloat = vector <@float 4>
.typedef @4xi32 = vector <@i32 4>
.typedef @2xdouble = vector <@double 2>
.const @4xI32_V1 <@4xi32> = VEC {@I32_0 @I32_1 @I32_2 @I32_3}
.const @4xI32_V2 <@4xi32> = VEC {@I32_4 @I32_5 @I32_6 @I32_7}
.const @4xF_V1 <@4xfloat> = VEC {@F_0 @F_1 @F_2 @F_3}
.const @4xF_V2 <@4xfloat> = VEC {@F_4 @F_5 @F_6 @F_7}
.const @2xD_V1 <@2xdouble> = VEC {@D_0 @D_1}
.const @2xD_V2 <@2xdouble> = VEC {@D_2 @D_3}
.funcsig @i_i = @i64 (@i64)
.funcsig @i_ii = @i64 (@i64 @i64)
.typedef @refvoid = ref<@void>
.typedef @irefvoid = iref<@void>
.typedef @weakrefvoid = weakref<@void>
.const @NULLREF <@refvoid> = NULL
.const @NULLIREF <@irefvoid> = NULL
.const @NULLFUNC <@funcdumb> = NULL
.const @NULLSTACK <@stack> = NULL
.typedef @refi8 = ref<@i8>
.typedef @irefi8 = iref<@i8>
.typedef @refi64 = ref<@i64>
.typedef @irefi64 = iref<@i64>