To protect your data, the CISO officer has suggested users to enable GitLab 2FA as soon as possible.

Commit 18eb5b94 authored by Kunshan Wang's avatar Kunshan Wang
Browse files

Work-around errno problem.

parent d5bf3607
...@@ -531,7 +531,7 @@ trait InstructionExecutor extends InterpreterActions with CommInstExecutor { ...@@ -531,7 +531,7 @@ trait InstructionExecutor extends InterpreterActions with CommInstExecutor {
val argBoxes = argList.map(boxOf) val argBoxes = argList.map(boxOf)
val shouldIncrementPC = curStack.callNative(sig, addr, argBoxes) val shouldIncrementPC = curStack.callNative(sig, addr, argBoxes, errnoBox)
if (shouldIncrementPC) { if (shouldIncrementPC) {
continueNormally() continueNormally()
} }
......
...@@ -54,6 +54,17 @@ class InterpreterThread(val id: MuInternalID, initialStack: InterpreterStack, in ...@@ -54,6 +54,17 @@ class InterpreterThread(val id: MuInternalID, initialStack: InterpreterStack, in
} }
} }
/**
* This box holds the per-mu-thread errno. It's a Hack.
* <p>
* Every InterpreterThread holds an ErrnoBox.
* Before every CCALL, set errno to savedLastError; after every CCALL, set savedLastError to errno. In this way,
* native programs see a "continuum" where the errno is never observed to change inside the micro VM.
*/
class ErrnoBox {
var errno: Int = 0
}
/** /**
* The states of an interpreter thread. * The states of an interpreter thread.
*/ */
...@@ -78,6 +89,9 @@ trait InterpreterThreadState { ...@@ -78,6 +89,9 @@ trait InterpreterThreadState {
/** Thread-local object reference. */ /** Thread-local object reference. */
val threadLocal = BoxRef(0L) val threadLocal = BoxRef(0L)
/** The per-thread errno. */
val errnoBox = new ErrnoBox
protected def curStack = stack.get protected def curStack = stack.get
protected def top: InterpreterFrame = curStack.top protected def top: InterpreterFrame = curStack.top
......
...@@ -210,11 +210,11 @@ class InterpreterStack(val id: MuInternalID, val stackMemory: StackMemory, stack ...@@ -210,11 +210,11 @@ class InterpreterStack(val id: MuInternalID, val stackMemory: StackMemory, stack
} }
/** Mu calling a native function. */ /** Mu calling a native function. */
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox]): Boolean = { def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox], errnoBox: ErrnoBox): Boolean = {
assert(top.isInstanceOf[MuFrame]) assert(top.isInstanceOf[MuFrame])
ensureNSK() ensureNSK()
pushNativeFrame(sig, func) pushNativeFrame(sig, func)
val result = maybeNSK.get.callNative(sig, func, args) val result = maybeNSK.get.callNative(sig, func, args, errnoBox)
handleNativeCallResult(result) handleNativeCallResult(result)
} }
......
...@@ -225,11 +225,20 @@ class NativeCallHelper { ...@@ -225,11 +225,20 @@ class NativeCallHelper {
* to Mu (to JVM). * to Mu (to JVM).
*/ */
val currentNativeStackKeeper = new ThreadLocal[NativeStackKeeper]() val currentNativeStackKeeper = new ThreadLocal[NativeStackKeeper]()
/**
* The saved errno.
* <p>
* Before every CCALL, set errno to savedLastError; after every CCALL, set savedLastError to errno. In this way,
* native programs see a "continuum" where the errno is never observed to change inside the micro VM.
*/
var savedLastError: Int = 0
/** /**
* Call a native function. Must be called by a NativeStackKeeper.Slave thread. * Call a native function. Must be called by a NativeStackKeeper.Slave thread.
*/ */
def callNative(nsk: NativeStackKeeper, sig: FuncSig, func: Word, args: Seq[ValueBox]): Option[ValueBox] = { def callNative(nsk: NativeStackKeeper, sig: FuncSig, func: Word, args: Seq[ValueBox],
errnoBox: ErrnoBox): Option[ValueBox] = {
assert(Thread.currentThread() == nsk.slaveThread) assert(Thread.currentThread() == nsk.slaveThread)
val jFunc = jffiFuncPool((sig, func)) val jFunc = jffiFuncPool((sig, func))
...@@ -244,53 +253,76 @@ class NativeCallHelper { ...@@ -244,53 +253,76 @@ class NativeCallHelper {
assert(currentNativeStackKeeper.get() == nsk) assert(currentNativeStackKeeper.get() == nsk)
val inv = Invoker.getInstance val inv = Invoker.getInstance
logger.debug("Before CCALL, saved errno = %d".format(errnoBox.errno))
val maybeRvb = sig.retTys match { val maybeRvb = sig.retTys match {
case Seq() => { case Seq() => {
NativeSupport.jffiLastError.set(errnoBox.errno)
inv.invokeLong(jFunc, hib) inv.invokeLong(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
None None
} }
case Seq(t) => { case Seq(t) => {
val b = t match { val b = t match {
case TypeInt(8) => { case TypeInt(8) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeInt(jFunc, hib).toByte val rv = inv.invokeInt(jFunc, hib).toByte
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 8)) BoxInt(OpHelper.trunc(BigInt(rv), 8))
} }
case TypeInt(16) => { case TypeInt(16) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeInt(jFunc, hib).toShort val rv = inv.invokeInt(jFunc, hib).toShort
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 16)) BoxInt(OpHelper.trunc(BigInt(rv), 16))
} }
case TypeInt(32) => { case TypeInt(32) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeInt(jFunc, hib) val rv = inv.invokeInt(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 32)) BoxInt(OpHelper.trunc(BigInt(rv), 32))
} }
case TypeInt(64) => { case TypeInt(64) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeLong(jFunc, hib) val rv = inv.invokeLong(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxInt(OpHelper.trunc(BigInt(rv), 64)) BoxInt(OpHelper.trunc(BigInt(rv), 64))
} }
case TypeFloat() => { case TypeFloat() => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeFloat(jFunc, hib) val rv = inv.invokeFloat(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxFloat(rv) BoxFloat(rv)
} }
case TypeDouble() => { case TypeDouble() => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeDouble(jFunc, hib) val rv = inv.invokeDouble(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxDouble(rv) BoxDouble(rv)
} }
case TypeStruct(flds) => { case TypeStruct(flds) => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeStruct(jFunc, hib) val rv = inv.invokeStruct(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
val buf = ByteBuffer.wrap(rv).order(ByteOrder.LITTLE_ENDIAN) val buf = ByteBuffer.wrap(rv).order(ByteOrder.LITTLE_ENDIAN)
logger.debug("Hexdump:\n" + HexDump.dumpByteBuffer(buf)) logger.debug("Hexdump:\n" + HexDump.dumpByteBuffer(buf))
val ptr = Pointer.wrap(NativeSupport.jnrRuntime, buf) val ptr = Pointer.wrap(NativeSupport.jnrRuntime, buf)
makeBoxFromPtr(ptr, 0, t) makeBoxFromPtr(ptr, 0, t)
} }
case _: AbstractPointerType => { case _: AbstractPointerType => {
NativeSupport.jffiLastError.set(errnoBox.errno)
val rv = inv.invokeAddress(jFunc, hib) val rv = inv.invokeAddress(jFunc, hib)
errnoBox.errno = NativeSupport.jffiLastError.get()
BoxPointer(rv) BoxPointer(rv)
} }
} }
Some(b) Some(b)
} }
} }
logger.debug("After CCALL, saved errno = %d".format(errnoBox.errno))
currentNativeStackKeeper.remove() currentNativeStackKeeper.remove()
maybeRvb maybeRvb
......
package uvm.refimpl.nat package uvm.refimpl.nat
import com.kenai.jffi.ClosureManager import com.kenai.jffi.ClosureManager
import com.kenai.jffi.LastError
import jnr.ffi.provider.jffi.NativeRuntime
import com.kenai.jffi.MemoryIO import com.kenai.jffi.MemoryIO
import jnr.ffi.Pointer import jnr.ffi.Pointer
import jnr.ffi.provider.jffi.NativeRuntime
/** /**
* Holder of JNR-specific resources. * Holder of JNR-specific resources.
...@@ -17,6 +18,7 @@ object NativeSupport { ...@@ -17,6 +18,7 @@ object NativeSupport {
// This is from JFFI, not JNR-FFI. // This is from JFFI, not JNR-FFI.
val jffiClosureManager = ClosureManager.getInstance() val jffiClosureManager = ClosureManager.getInstance()
val jffiLastError = LastError.getInstance()
// Explicit memory management // Explicit memory management
......
...@@ -11,6 +11,7 @@ import uvm.{ Function => MFunc } ...@@ -11,6 +11,7 @@ import uvm.{ Function => MFunc }
import uvm.refimpl.UvmRuntimeException import uvm.refimpl.UvmRuntimeException
import uvm.refimpl.Word import uvm.refimpl.Word
import uvm.refimpl.itpr.ValueBox import uvm.refimpl.itpr.ValueBox
import uvm.refimpl.itpr.ErrnoBox
object PoorManAgent { object PoorManAgent {
val logger = Logger(LoggerFactory.getLogger(getClass.getName)) val logger = Logger(LoggerFactory.getLogger(getClass.getName))
...@@ -70,7 +71,7 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo ...@@ -70,7 +71,7 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
abstract class ToSlave abstract class ToSlave
object ToSlave { object ToSlave {
/** Mu wants to call a native function. */ /** Mu wants to call a native function. */
case class CallNative(sig: FuncSig, func: Word, args: Seq[ValueBox]) extends ToSlave case class CallNative(sig: FuncSig, func: Word, args: Seq[ValueBox], errnoBox: ErrnoBox) extends ToSlave
/** Mu wants to return to a native function. maybeRvb holds the return value. */ /** Mu wants to return to a native function. maybeRvb holds the return value. */
case class ReturnToNative(maybeRvb: Option[ValueBox]) extends ToSlave case class ReturnToNative(maybeRvb: Option[ValueBox]) extends ToSlave
/** Mu wants the slave to stop. */ /** Mu wants the slave to stop. */
...@@ -92,8 +93,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo ...@@ -92,8 +93,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
return return
} }
received match { received match {
case ToSlave.CallNative(sig, func, args) => { case ToSlave.CallNative(sig, func, args, errnoBox) => {
val maybeRvb = nativeCallHelper.callNative(master, sig, func, args) val maybeRvb = nativeCallHelper.callNative(master, sig, func, args, errnoBox)
master.send(NativeCallResult.ReturnToMu(maybeRvb)) master.send(NativeCallResult.ReturnToMu(maybeRvb))
receiving() receiving()
} }
...@@ -129,8 +130,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo ...@@ -129,8 +130,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
throw e throw e
} }
received match { received match {
case ToSlave.CallNative(sig, func, args) => { case ToSlave.CallNative(sig, func, args, errnoBox) => {
val maybeRvb = nativeCallHelper.callNative(master, sig, func, args) val maybeRvb = nativeCallHelper.callNative(master, sig, func, args, errnoBox)
master.send(NativeCallResult.ReturnToMu(maybeRvb)) master.send(NativeCallResult.ReturnToMu(maybeRvb))
receiving() receiving()
} }
...@@ -162,8 +163,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo ...@@ -162,8 +163,8 @@ class NativeStackKeeper(implicit nativeCallHelper: NativeCallHelper) extends Poo
val slaveThread = new Thread(slave) val slaveThread = new Thread(slave)
slaveThread.start() slaveThread.start()
def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox]): NativeCallResult = { def callNative(sig: FuncSig, func: Word, args: Seq[ValueBox], errnoBox: ErrnoBox): NativeCallResult = {
slave.send(ToSlave.CallNative(sig, func, args)) slave.send(ToSlave.CallNative(sig, func, args, errnoBox))
val result = receive() val result = receive()
result result
} }
......
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.Word
import ch.qos.logback.classic.Level._
import uvm.refimpl.UvmBundleTesterBase
import com.kenai.jffi.Library
import jnr.posix.POSIXFactory
import uvm.refimpl.nat.NativeLibraryTestHelper
class UvmInterpreterNativeErrnoTest extends UvmBundleTesterBase {
setLogLevels(
ROOT_LOGGER_NAME -> INFO,
"uvm.refimpl.itpr" -> DEBUG,
"uvm.refimpl.nat" -> DEBUG
)
NativeLibraryTestHelper.loadTestLibraryToMicroVM(microVM, "simplecfuncs")
preloadBundles("tests/uvm-refimpl-test/primitives.uir", "tests/uvm-refimpl-test/native-errno-test.uir")
"The CCALL instruction" should "save errno to thread's errno box" in {
val ctx = microVM.newContext()
val func = ctx.handleFromFunc("@errno_test")
val a0 = ctx.handleFromInt(42, 32)
testFunc(ctx, func, Seq(a0)) { (ctx, th, st, wp) =>
val Seq(rv: MuIntValue) = ctx.dumpKeepalives(st, 0)
ctx.handleToSInt(rv) shouldEqual 42
returnFromTrap(st)
}
ctx.closeContext()
}
}
\ No newline at end of file
package uvm.refimpl.nat package uvm.refimpl.nat
import com.kenai.jffi.Library import com.kenai.jffi.Library
import uvm.refimpl.MicroVM
object NativeLibraryTestHelper { object NativeLibraryTestHelper {
def loadTestLibrary(name: String): Library = { def getLibPath(name: String): String = {
val relPath = s"tests/c-snippets/${name}.so" val relPath = s"tests/c-snippets/${name}.so"
if (!new java.io.File(relPath).isFile()) { if (!new java.io.File(relPath).isFile()) {
throw new RuntimeException(s"Need to compile the ${name}.so library. cd into tests/c-snippets and invoke 'make'.") throw new RuntimeException(s"Need to compile the ${name}.so library. cd into tests/c-snippets and invoke 'make'.")
} }
relPath
}
def loadTestLibrary(name: String): Library = {
val relPath = getLibPath(name)
Library.openLibrary(relPath, Library.NOW) Library.openLibrary(relPath, Library.NOW)
} }
def loadTestLibraryToMicroVM(microVM: MicroVM, name: String): Unit = {
val relPath = getLibPath(name)
microVM.nativeLibraryHolder.loadLibrary(relPath)
}
} }
\ No newline at end of file
...@@ -52,7 +52,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers { ...@@ -52,7 +52,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers {
val box1 = BoxInt(3) val box1 = BoxInt(3)
val box2 = BoxInt(4) val box2 = BoxInt(4)
val result = nsk.callNative(sig, addr, Seq(box1, box2)) val result = nsk.callNative(sig, addr, Seq(box1, box2), new ErrnoBox)
result shouldBeA[NativeCallResult.ReturnToMu] { its => result shouldBeA[NativeCallResult.ReturnToMu] { its =>
its.maybeRvb shouldBe Some(BoxInt(7)) its.maybeRvb shouldBe Some(BoxInt(7))
...@@ -84,7 +84,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers { ...@@ -84,7 +84,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers {
val b1 = BoxDouble(3.0) val b1 = BoxDouble(3.0)
val b2 = BoxPointer(mockClosAddr) val b2 = BoxPointer(mockClosAddr)
val r1 = nsk.callNative(sig, addr, Seq(b1, b2)) val r1 = nsk.callNative(sig, addr, Seq(b1, b2), new ErrnoBox)
println("Hello. I received r1") println("Hello. I received r1")
...@@ -145,7 +145,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers { ...@@ -145,7 +145,7 @@ class NativeStackKeeperTest extends UvmTestBase with ExtraMatchers {
val b2 = BoxPointer(closHandle.getAddress) val b2 = BoxPointer(closHandle.getAddress)
val br = BoxDouble(-1.0) val br = BoxDouble(-1.0)
val result = nsk.callNative(sig, addr, Seq(b1, b2)) val result = nsk.callNative(sig, addr, Seq(b1, b2), new ErrnoBox)
closHandle.dispose() closHandle.dispose()
......
#include<stdio.h> #include<stdio.h>
#include<errno.h>
int hello_world(int n) { int hello_world(int n) {
printf("Hello world from C!\n"); printf("Hello world from C!\n");
return n + 1; return n + 1;
} }
void set_errno(int n) {
printf("Hi! I am about to set errno to %d\n", n);
errno = n;
}
int get_errno() {
return errno;
}
// require "primitives.uir"
.funcsig @set_errno.sig = (@i32) -> ()
.funcsig @get_errno.sig = () -> (@i32)
.typedef @set_errno.fp = ufuncptr<@set_errno.sig>
.typedef @get_errno.fp = ufuncptr<@get_errno.sig>
.const @set_errno <@set_errno.fp> = EXTERN "set_errno"
.const @get_errno <@get_errno.fp> = EXTERN "get_errno"
.funcsig @errno_test.sig = (@i32) -> ()
.funcdef @errno_test VERSION %1 <@errno_test.sig> {
%entry(<@i32> %p0):
CCALL #DEFAULT <@set_errno.fp @set_errno.sig> @set_errno (%p0)
%rv = CCALL #DEFAULT <@get_errno.fp @get_errno.sig> @get_errno ()
[%trap] TRAP <> KEEPALIVE (%rv)
COMMINST @uvm.thread_exit
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment