Commit 5eb904bb authored by Kunshan Wang's avatar Kunshan Wang

Merge branch 'issue60-symbol-reloc'

parents 25c6d5b9 f0c46abf
......@@ -265,11 +265,6 @@ struct MuVM {
// Set handlers
void (*set_trap_handler)(MuVM *mvm, MuTrapHandler trap_handler, MuCPtr userdata);
// Build boot image
void (*make_boot_image)(MuVM *mvm,
MuID* whitelist, MuArraySize whitelist_sz,
MuCString output_file); /// MUAPIPARSER whitelist:array:whitelist_sz
// Proprietary API of refimpl2: Let the micro VM execute.
void (*execute)(MuVM *mvm);
......@@ -426,8 +421,9 @@ struct MuCtx {
void (*disable_watchpoint)(MuCtx *ctx, MuWPID wpid);
// Mu memory pinning, usually object pinning
MuUPtrValue (*pin )(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
void (*unpin)(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
MuUPtrValue (*pin )(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
void (*unpin )(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
MuUPtrValue (*get_addr)(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
// Expose Mu functions as native callable things, usually function pointers
MuValue (*expose )(MuCtx *ctx, MuFuncRefValue func, MuCallConv call_conv, MuIntValue cookie);
......@@ -435,6 +431,15 @@ struct MuCtx {
// Create an IR builder and start building.
MuIRBuilder* (*new_ir_builder)(MuCtx *ctx);
// Build boot image
void (*make_boot_image)(MuCtx *ctx,
MuID* whitelist, MuArraySize whitelist_sz,
MuFuncRefValue primordial_func, MuStackRefValue primordial_stack,
MuRefValue primordial_threadlocal,
MuIRefValue *sym_fields, MuCString *sym_strings, MuArraySize nsyms,
MuIRefValue *reloc_fields, MuCString *reloc_strings, MuArraySize nrelocs,
MuCString output_file); /// MUAPIPARSER whitelist:array:whitelist_sz;sym_fields:array:nsyms;sym_strings:array:nsyms;reloc_fields:array:nrelocs;reloc_strings:array:nrelocs;primordial_func:optional;primordial_stack:optional;primordial_threadlocal:optional
};
// These types are all aliases of MuID. They are provided to guide C
......@@ -758,9 +763,10 @@ struct MuIRBuilder {
#define MU_CI_UVM_KILL_DEPENDENCY ((MuCommInst)0x230) /// MUAPIPARSER muname:@uvm.kill_dependency
#define MU_CI_UVM_NATIVE_PIN ((MuCommInst)0x240) /// MUAPIPARSER muname:@uvm.native.pin
#define MU_CI_UVM_NATIVE_UNPIN ((MuCommInst)0x241) /// MUAPIPARSER muname:@uvm.native.unpin
#define MU_CI_UVM_NATIVE_EXPOSE ((MuCommInst)0x242) /// MUAPIPARSER muname:@uvm.native.expose
#define MU_CI_UVM_NATIVE_UNEXPOSE ((MuCommInst)0x243) /// MUAPIPARSER muname:@uvm.native.unexpose
#define MU_CI_UVM_NATIVE_GET_COOKIE ((MuCommInst)0x244) /// MUAPIPARSER muname:@uvm.native.get_cookie
#define MU_CI_UVM_NATIVE_GET_ADDR ((MuCommInst)0x242) /// MUAPIPARSER muname:@uvm.native.get_addr
#define MU_CI_UVM_NATIVE_EXPOSE ((MuCommInst)0x243) /// MUAPIPARSER muname:@uvm.native.expose
#define MU_CI_UVM_NATIVE_UNEXPOSE ((MuCommInst)0x244) /// MUAPIPARSER muname:@uvm.native.unexpose
#define MU_CI_UVM_NATIVE_GET_COOKIE ((MuCommInst)0x245) /// MUAPIPARSER muname:@uvm.native.get_cookie
#define MU_CI_UVM_META_ID_OF ((MuCommInst)0x250) /// MUAPIPARSER muname:@uvm.meta.id_of
#define MU_CI_UVM_META_NAME_OF ((MuCommInst)0x251) /// MUAPIPARSER muname:@uvm.meta.name_of
#define MU_CI_UVM_META_LOAD_BUNDLE ((MuCommInst)0x252) /// MUAPIPARSER muname:@uvm.meta.load_bundle
......
......@@ -11,6 +11,7 @@ const char *hw_string = "Hello world!\n";
const char *hw2_string = "Goodbye world!\n";
const char *gc_conf =
"vmLog=DEBUG\n"
"sosSize=524288\n"
"losSize=524288\n"
"globalSize=1048576\n"
......@@ -18,8 +19,10 @@ const char *gc_conf =
;
int main() {
printf("Creating micro VM...\n");
MuVM *mvm = mu_refimpl2_new_ex(gc_conf);
printf("Creating client context...\n");
MuCtx *ctx = mvm->new_context(mvm);
char *bundle1 =
......
......@@ -188,6 +188,7 @@ _array_converters = {
"uint64_t*" : "readLongArray",
"MuFlag*" : "readFlagArray",
"MuID*" : "readIntArray",
"MuCString*": "readCStringArray",
}
_special_converters = {
......
......@@ -363,9 +363,10 @@ common_instruction_opcodes = {
'@uvm.kill_dependency': 0x230,
'@uvm.native.pin': 0x240,
'@uvm.native.unpin': 0x241,
'@uvm.native.expose': 0x242,
'@uvm.native.unexpose': 0x243,
'@uvm.native.get_cookie': 0x244,
'@uvm.native.get_addr': 0x242,
'@uvm.native.expose': 0x243,
'@uvm.native.unexpose': 0x244,
'@uvm.native.get_cookie': 0x245,
'@uvm.meta.id_of': 0x250,
'@uvm.meta.name_of': 0x251,
'@uvm.meta.load_bundle': 0x252,
......@@ -1499,7 +1500,6 @@ _initialize_methods(MuVM, [
('id_of', CMuID, [CMuName]),
('name_of', CMuName, [CMuID]),
('set_trap_handler_', None, [CMuTrapHandler, CMuCPtr]),
('make_boot_image_', None, [ctypes.c_void_p, CMuArraySize, CMuCString]),
('execute', None, []),
('get_mu_error_ptr', ctypes.c_void_p, []),
## GEN:END:MuVM
......@@ -1591,9 +1591,11 @@ _initialize_methods(MuCtx, [
('disable_watchpoint', None, [CMuWPID]),
('pin', MuUPtrValue, [MuValue]),
('unpin', None, [MuValue]),
('get_addr', MuUPtrValue, [MuValue]),
('expose', MuValue, [MuFuncRefValue, CMuCallConv, MuIntValue]),
('unexpose', None, [CMuCallConv, MuValue]),
('new_ir_builder', MuIRBuilder, []),
('make_boot_image_', None, [ctypes.c_void_p, CMuArraySize, MuFuncRefValue, MuStackRefValue, MuRefValue, ctypes.c_void_p, ctypes.c_void_p, CMuArraySize, ctypes.c_void_p, ctypes.c_void_p, CMuArraySize, CMuCString]),
## GEN:END:MuCtx
])
......
......@@ -22,6 +22,7 @@ import uvm.refimpl.nat.NativeLibraryHolder
import uvm.staticanalysis.StaticAnalyzer
import uvm.utils.IDFactory
import uvm.utils.WithUtils.tryWithResource
import uvm.refimpl.bootimg.PrimordialInfo
object MicroVM {
val DEFAULT_SOS_SIZE: Word = 2L * 1024L * 1024L; // 2MiB
......@@ -31,16 +32,29 @@ object MicroVM {
def apply(): MicroVM = {
val vmConf = new VMConf()
new MicroVM(vmConf)
MicroVM(vmConf)
}
def apply(confStr: String): MicroVM = {
val vmConf = VMConf(confStr)
new MicroVM(vmConf)
MicroVM(vmConf)
}
def apply(confStr: String, appArgs: Seq[String]): MicroVM = {
val vmConf = VMConf(confStr)
MicroVM(vmConf, appArgs)
}
def apply(vmConf: VMConf): MicroVM = {
new MicroVM(vmConf, None)
}
def apply(vmConf: VMConf, appArgs: Seq[String]): MicroVM = {
new MicroVM(vmConf, Some(appArgs))
}
}
class MicroVM(val vmConf: VMConf) extends IRBuilderListener {
class MicroVM private (val vmConf: VMConf, val appArgs: Option[Seq[String]]) extends IRBuilderListener {
// implicitly injected resources
private implicit val microVM = this
......@@ -177,17 +191,9 @@ class MicroVM(val vmConf: VMConf) extends IRBuilderListener {
* Make boot image.
*/
def makeBootImage(whiteList: Seq[MuID], outputFile: String): Unit = {
val whiteListObjs = whiteList.map { id =>
val obj = globalBundle.allNs(id)
obj match {
case tl: TopLevel => tl
case _ => throw new UvmRuntimeException(
"White list may only contain top-level definitions. Entity ID %d%s is a %s".format(
obj.id, obj.name.map(n => s" (${n})").getOrElse(""), obj.getClass.getName))
}
}
val whiteListObjs = whiteList.map(globalBundle.topLevelNs.apply)
bootImageBuilder.makeBootImage(whiteListObjs, outputFile)
bootImageBuilder.makeBootImage(whiteListObjs, PrimordialInfo.NO_PRIMORDIAL, Seq(), Seq(), outputFile)
}
/**
......@@ -209,7 +215,7 @@ class MicroVM(val vmConf: VMConf) extends IRBuilderListener {
* Load from a boot image.
*/
def loadBootImage(file: String): Unit = {
tryWithResource(new BootImageLoader(file)) { bil =>
tryWithResource(new BootImageLoader(file, appArgs)) { bil =>
bil.load()
}
}
......
......@@ -20,6 +20,8 @@ import uvm.ssavariables.AtomicRMWOptr._
import uvm.ssavariables.HasKeepaliveClause
import uvm.ssavariables.MemoryOrder._
import uvm.types._
import uvm.refimpl.bootimg.FieldAndSymbol
import uvm.refimpl.bootimg.PrimordialInfo
object MuCtx {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
......@@ -189,9 +191,9 @@ class MuCtx(val ctxID: MuInternalID, _mutator: Mutator)(
/** Compare general reference types for equality. */
def refEq(lhs: MuGenRefValue, rhs: MuGenRefValue): Boolean = (lhs, rhs) match {
case (l: MuRefValue, r: MuRefValue) => l.vb.objRef == r.vb.objRef
case (l: MuIRefValue, r: MuIRefValue) => l.vb.oo == r.vb.oo
case (l: MuOpaqueRefValue[_], r: MuOpaqueRefValue[_]) => l.vb.obj == r.vb.obj
case (l: MuRefValue, r: MuRefValue) => l.vb.objRef == r.vb.objRef
case (l: MuIRefValue, r: MuIRefValue) => l.vb.oo == r.vb.oo
case (l: MuOpaqueRefValue[_], r: MuOpaqueRefValue[_]) => l.vb.obj == r.vb.obj
case (l, r) => {
throw new IllegalArgumentException("Bad types for refEq: %s and %s".format(
l.showTy, r.showTy))
......@@ -269,11 +271,11 @@ class MuCtx(val ctxID: MuInternalID, _mutator: Mutator)(
val nt = microVM.globalBundle.typeNs(newType)
val nh = (opnd, nt) match {
case (MuRefValue(ty, vb), ty2 @ TypeRef(_)) => MuRefValue(ty2, vb)
case (MuIRefValue(ty, vb), ty2 @ TypeIRef(_)) => MuIRefValue(ty2, vb)
case (MuRefValue(ty, vb), ty2 @ TypeRef(_)) => MuRefValue(ty2, vb)
case (MuIRefValue(ty, vb), ty2 @ TypeIRef(_)) => MuIRefValue(ty2, vb)
case (MuOpaqueRefValue(ty, vb), ty2 @ TypeFuncRef(_)) => {
require(ty.isInstanceOf[TypeFuncRef] && vb.isInstanceOf[BoxOpaque[_]],
"Attempt to cast %s to funcref. funcref value required".format(opnd.showTy))
"Attempt to cast %s to funcref. funcref value required".format(opnd.showTy))
MuFuncRefValue(ty2, vb.asInstanceOf[BoxFunc])
}
case _ => {
......@@ -680,6 +682,24 @@ class MuCtx(val ctxID: MuInternalID, _mutator: Mutator)(
unpin(objRef)
}
def getAddr(loc: MuValue): MuUPtrValue = {
val (objTy, (objRef, offset)) = loc match {
case MuRefValue(t, vb) => (t, (vb.objRef, 0L))
case MuIRefValue(t, vb) => (t, vb.oo)
case _ => {
throw new IllegalArgumentException("loc must be ref or iref. Found %s".format(loc.ty))
}
}
// do nothing
val addr = objRef + offset
assert(microVM.memoryManager.globalMemory.isInSpace(addr) || pinSet.contains(objRef),
"Attempt to get address of objref %d 0x%x, offset %d, 0x%x, which is neither a global cell nor pinned by the current thread.".format(
objRef, objRef, offset, offset))
val ptrTy = InternalTypePool.ptrOf(objTy)
val box = new BoxPointer(addr)
addHandle(MuUPtrValue(ptrTy, box))
}
def expose(func: MuFuncRefValue, callConv: Flag, cookie: MuIntValue): MuUFPValue = {
val TypeFuncRef(sig) = func.ty
val f = func.vb.obj.getOrElse {
......@@ -701,6 +721,37 @@ class MuCtx(val ctxID: MuInternalID, _mutator: Mutator)(
microVM.newIRBuilder()
}
def makeBootImage(whiteList: Seq[MuID],
primordialFunc: Option[MuFuncRefValue], primordialStack: Option[MuStackRefValue], primordialThreadLocal: Option[MuRefValue],
symFields: Seq[MuIRefValue], symStrings: Seq[String], relocFields: Seq[MuIRefValue], relocStrings: Seq[String],
outputFile: String): Unit = {
val whiteListObjs = whiteList.map(microVM.globalBundle.topLevelNs.apply)
val maybePF = primordialFunc.flatMap(_.vb.obj)
val maybePS = primordialStack.flatMap(_.vb.obj)
val maybeTL = primordialThreadLocal.flatMap { h =>
h.vb.objRef match {
case 0 => None
case a => Some(a)
}
}
val primordial = PrimordialInfo(maybePF, maybePS, maybeTL)
val symFlds = symFields.map { h => (h.vb.objRef, h.vb.offset) }
val relocFlds = relocFields.map { h =>
assert(h.ty.ty.isInstanceOf[AbstractPointerType], "Reloc field %d 0x%x is not a pointer field. field type: %s".format(
h.vb.addr, h.vb.addr, h.ty.ty))
(h.vb.objRef, h.vb.offset)
}
val syms = for (((obj, off), name) <- symFlds zip symStrings) yield FieldAndSymbol(obj, off, name)
val relocs = for (((obj, off), name) <- relocFlds zip relocStrings) yield FieldAndSymbol(obj, off, name)
microVM.bootImageBuilder.makeBootImage(whiteListObjs, primordial, syms, relocs, outputFile)
}
// Internal methods for the micro VM
def handleFromInterpreterThread(thr: Option[InterpreterThread]): MuThreadRefValue = {
......
package uvm.refimpl.bootimg
import uvm.types.Type
import uvm.refimpl.Word
object BootImageFile {
val FILEEXT_DATA = ".data"
val FILEEXT_ALLOC = ".alloc"
val FILEEXT_RELOC = ".reloc"
val UIRBUNDLE_FILE = "bundle.uir"
val IDNAMEMAP_FILE = "idnamemap"
val METAINFO_FILE = "metainfo"
val KEY_INITFUNC = "initfunc"
val KEY_THREADLOCAL = "threadlocal"
val KEY_EXTRALIBS = "extralibs"
val AS_REF = "R"
val AS_IREF = "I"
val AS_TAGREF = "T"
val AS_FUNCREF = "F"
val AS_UPTR = "P"
val TO_GLOBAL = "G"
val TO_HEAP = "H"
val TO_FUNC = "F"
val TO_SYM = "S"
val NO_OFFSET = 0L
val NO_SYM = ""
/**
* Allocation record. For global and heap.
*
* @param num An integer to identify the unit in a .data file. For global, it is the ID of the global cell; for heap
* object, it is a sequential number.
* @param fileOffset The offset from the beginning of the .data file.
* @param ty The type of the allocation unit.
* @param varLen The length of the variable part if it is a hybrid; otherwise 0.
*
* @param addr The address in the current micro VM instance. Not persisted.
*/
case class UnitAllocRecord(num: Long, fileOffset: Long, ty: Type, varLen: Long, addr: Word)
/**
* Relocation record. One for each field of ref<T>, iref<T>, weakref<T> or funcref<sig> types.
*
* @param num The source allocation unit number.
* @param fieldOffset The offset of the field from the beginning of the allocation unit.
* @param targetKind Whether the target is in the global memory, heap, or is a function.
* @param targetNum To identify the target. For memory, it is the beginning of the allocation unit; for function, it
* is the ID of the function.
* @param targetOffset If the source is an iref<T>, it is the offset from the beginning of the target allocation unit;
* otherwise 0.
*/
case class FieldRelocRecord(num: Long, fieldOffset: Long, fieldKind: String, targetKind: String, targetNum: Long, targetOffset: Long, targetString: String)
}
\ No newline at end of file
......@@ -24,15 +24,28 @@ import uvm.types.TypeHybrid
import uvm.utils.IOHelpers.forEachLine
import uvm.utils.MappedIDFactory
import uvm.utils.WithUtils.tryWithResource
import scala.collection.mutable.ArrayBuffer
import uvm.refimpl.itpr.HowToResume
import uvm.refimpl.cmdline.NativeArgv
import uvm.refimpl.itpr.BoxInt
import uvm.refimpl.InternalTypePool
import uvm.refimpl.InternalTypes
import uvm.refimpl.itpr.BoxPointer
import uvm.utils.IOHelpers
import com.typesafe.scalalogging.Logger
import org.slf4j.LoggerFactory
import uvm.refimpl.mem.HeaderUtils
object BootImageLoader {
val MUTATOR_NAME = "bootimgldr"
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
val HEAP_INIT_MUTATOR_NAME = "boot-image-loader-heap-init"
val PRIMORDIAL_INIT_MUTATOR_NAME = "boot-image-loader-primordial"
val EXTRALIBS_FILE = "extralibs"
}
class BootImageLoader(file: String)(implicit microVM: MicroVM) extends AutoCloseable {
class BootImageLoader(file: String, maybeAppArgs: Option[Seq[String]])(implicit microVM: MicroVM) extends AutoCloseable {
import BootImageLoader._
import BootImageWriter._
import BootImageFile._
private val globalBundle = microVM.globalBundle
private val memoryManager = microVM.memoryManager
......@@ -59,25 +72,38 @@ class BootImageLoader(file: String)(implicit microVM: MicroVM) extends AutoClose
private def zipFile(name: String): InputStream = zipFile(zip.getEntry(name))
private def zipFileText(name: String): Reader = new InputStreamReader(zipFile(name), StandardCharsets.UTF_8)
private def zipFileTextBuf(name: String): BufferedReader = new BufferedReader(zipFileText(name))
override def close(): Unit = {
zip.close()
}
def load(): Unit = {
loadExtraLibs()
logger.info("Loading boot image: {}".format(file))
val metaInfo = loadMetaInfo()
loadExtraLibs(metaInfo)
loadUIRBundle()
initializeMemory()
maybeCreatePrimordialThread(metaInfo)
}
private def loadExtraLibs(): Unit = {
Option(zip.getEntry(EXTRALIBS_FILE)) foreach { entry =>
val line = tryWithResource(zipFileTextBuf(EXTRALIBS_FILE)) { br =>
br.readLine()
}
private def loadMetaInfo(): Map[String, String] = {
val lines = tryWithResource(zipFileTextBuf(METAINFO_FILE)) { br =>
IOHelpers.readLines(br)
}
val pairs = lines.map { line =>
val Array(k, v) = line.split("=", -1)
(k, v)
}
pairs.toMap
}
private def loadExtraLibs(metaInfo: Map[String, String]): Unit = {
for (line <- metaInfo.get("extralibs")) {
val libs = line.split(":")
for (lib <- libs) {
......@@ -143,7 +169,7 @@ class BootImageLoader(file: String)(implicit microVM: MicroVM) extends AutoClose
ais.readToMemory(addr, size)
}
tryWithResource(heap.makeMutator(MUTATOR_NAME)) { mutator =>
tryWithResource(heap.makeMutator(HEAP_INIT_MUTATOR_NAME)) { mutator =>
forEachUnit("heap") { (ais, ar) =>
val UnitAllocRecord(num, fileOffset, ty, varLen, _) = ar
......@@ -188,45 +214,58 @@ class BootImageLoader(file: String)(implicit microVM: MicroVM) extends AutoClose
private def relocate(): Unit = {
forEachReloc("global") { rr =>
val FieldRelocRecord(num, fieldOffset, fieldKind, targetKind, targetNum, targetOffset) = rr
val FieldRelocRecord(num, fieldOffset, fieldKind, targetKind, targetNum, targetOffset, targetString) = rr
val base = globalNumToAddr(num)
relocField(base, fieldOffset, fieldKind, targetKind, targetNum, targetOffset)
relocField(base, fieldOffset, fieldKind, targetKind, targetNum, targetOffset, targetString)
}
forEachReloc("heap") { rr =>
val FieldRelocRecord(num, fieldOffset, fieldKind, targetKind, targetNum, targetOffset) = rr
val FieldRelocRecord(num, fieldOffset, fieldKind, targetKind, targetNum, targetOffset, targetString) = rr
val base = objNumToAddr(num)
relocField(base, fieldOffset, fieldKind, targetKind, targetNum, targetOffset)
relocField(base, fieldOffset, fieldKind, targetKind, targetNum, targetOffset, targetString)
}
}
private def forEachReloc(groupName: String)(f: FieldRelocRecord => Unit): Unit = {
tryWithResource(zipFileTextBuf(groupName + FILEEXT_RELOC)) { relocIS =>
forEachLine(relocIS) { line =>
val elems = line.split(",")
val elems = line.split(",", -1) // include the trailing empty string
val num = elems(0).toLong
val fieldOffset = elems(1).toLong
val fieldKind = elems(2)
val targetKind = elems(3)
val targetNum = elems(4).toLong
val targetOffset = elems(5).toLong
val rr = FieldRelocRecord(num, fieldOffset, fieldKind, targetKind, targetNum, targetOffset)
val targetString = elems(6)
val rr = FieldRelocRecord(num, fieldOffset, fieldKind, targetKind, targetNum, targetOffset, targetString)
f(rr)
}
}
}
private def relocField(srcBase: Word, srcOffset: Word, fieldKind: String, targetKind: String, targetNum: Long, targetOffset: Word): Unit = {
private def relocField(srcBase: Word, srcOffset: Word, fieldKind: String,
targetKind: String, targetNum: Long, targetOffset: Word, targetSymbol: String): Unit = {
val srcFieldAddr = srcBase + srcOffset
fieldKind match {
case AS_UPTR => {
assert(targetKind != TO_FUNC, "BUG: uptr field referring to func")
assert(targetKind != TO_HEAP, "BUG: uptr field referring to obj")
val targetAddr = targetKind match {
case TO_GLOBAL => getTargetMemBase(targetKind, targetNum) + targetOffset
case TO_SYM => getSymbolAddr(targetSymbol)
}
memorySupport.storeLong(srcFieldAddr, targetAddr)
}
case AS_REF => {
assert(targetOffset == 0L)
assert(targetKind != TO_FUNC, "BUG: ref field referring to function")
assert(targetKind != TO_SYM, "BUG: ref field referring to symbol")
val targetBase = getTargetMemBase(targetKind, targetNum)
memorySupport.storeLong(srcFieldAddr, targetBase)
}
case AS_IREF => {
assert(targetKind != TO_FUNC, "BUG: iref field referring to function")
assert(targetKind != TO_SYM, "BUG: iref field referring to symbol")
val targetBase = getTargetMemBase(targetKind, targetNum)
val (irefBase, irefOffset) = targetKind match {
case TO_GLOBAL => (0L, targetBase + targetOffset)
......@@ -238,6 +277,7 @@ class BootImageLoader(file: String)(implicit microVM: MicroVM) extends AutoClose
case AS_TAGREF => {
assert(targetOffset == 0L)
assert(targetKind != TO_FUNC, "BUG: tagref64 field referring to function")
assert(targetKind != TO_SYM, "BUG: tagref64 field referring to symbol")
val targetBase = getTargetMemBase(targetKind, targetNum)
val oldTR64 = memorySupport.loadLong(srcFieldAddr)
val oldTag = OpHelper.tr64ToTag(oldTR64)
......@@ -259,6 +299,53 @@ class BootImageLoader(file: String)(implicit microVM: MicroVM) extends AutoClose
case TO_HEAP => objNumToAddr(targetNum)
}
}
private def getSymbolAddr(targetSymbol: String): Word = {
microVM.nativeLibraryHolder.getSymbolAddress(targetSymbol)
}
private def maybeCreatePrimordialThread(metaInfo: Map[String, String]): Unit = {
metaInfo.get(KEY_INITFUNC).map(_.toInt) match {
case Some(initFuncID) => {
val func = microVM.globalBundle.funcNs(initFuncID)
val appArgs = maybeAppArgs.getOrElse {
throw new BootImageLoaderException("Boot image has a primordial thread, but the MicroVM instance is not created " +
"with command line arguments. Please use the tools/runmu.sh command line utility to run boot images that has " +
"primordial threads. In realistic scenarios, the primordial thread in the boot image is supposed to be the " +
"entry point of a program, thus primordial threads should only exist in STAND-ALONE EXECUTABLE boot iamges.")
}
val threadLocalAddr = metaInfo.get(KEY_THREADLOCAL).map(_.toLong).map { num =>
objNumToAddr(num)
}.getOrElse(0L)
tryWithResource(heap.makeMutator(PRIMORDIAL_INIT_MUTATOR_NAME)) { mutator =>
logger.info("Creating primordial thread using function %d...".format(initFuncID))
val func = microVM.globalBundle.funcNs(initFuncID)
logger.info("Stack-bottom function name: %s".format(func.name.getOrElse("(no name)")))
logger.info("Thread local obj address: %d 0x%x".format(threadLocalAddr, threadLocalAddr))
logger.info {
val tag = HeaderUtils.getTag(threadLocalAddr)
val ty = HeaderUtils.getType(microVM, tag)
"Thread-local obj type: %s %s".format(ty.repr, ty)
}
val stack = microVM.threadStackManager.newStack(func, mutator)
val allArgs = Seq(file) ++ appArgs
val nativeArgv = new NativeArgv(allArgs)
val argcBox = BoxInt(nativeArgv.argc)
val argvBox = BoxPointer(nativeArgv.argv)
val htr = HowToResume.PassValues(Seq(argcBox, argvBox))
val thread = microVM.threadStackManager.newThread(stack, threadLocalAddr, htr)
}
}
case None => {
logger.info("Boot image does not have a primordial thread (not an error).")
}
}
}
}
class AlignedInputStream(is: InputStream) extends Closeable {
......
......@@ -85,7 +85,8 @@ object TransitiveClosureBuilder {
case class GlobalCellRec(begin: Word, end: Word, g: GlobalCell)
}
class TransitiveClosureBuilder(initialSet: Seq[TopLevel])(implicit microVM: MicroVM) extends MemoryFieldHandler {
class TransitiveClosureBuilder(initialSet: Seq[TopLevel], val primordial: PrimordialInfo)(
implicit microVM: MicroVM) extends MemoryFieldHandler {
import TransitiveClosureBuilder._
private val globalMemory: Space = microVM.memoryManager.globalMemory
......@@ -103,8 +104,27 @@ class TransitiveClosureBuilder(initialSet: Seq[TopLevel])(implicit microVM: Micr
* Global cells + heap objects.
*/
val allocs = TransitiveClosure[Word]()
// Put the primordial function/stack and the thread-local into the set
{
val PrimordialInfo(pf, ps, ptl) = primordial
if (ps.isDefined) {
throw new BootImageBuilderException("Building boot image using a stack is not implemented. The current way is to specify the stack-bottom function.")
}
pf foreach { func =>
// If the primordial function/stack is specified, add it.
tls += func
ptl foreach { addr =>
// Also add the thread-local if specified.
allocs += addr
}
}
}
def getGlobalCellAddr(g: GlobalCell): Word = {
private def getGlobalCellAddr(g: GlobalCell): Word = {
microVM.constantPool.getGlobalVarBox(g).asInstanceOf[BoxIRef].addr
}
......@@ -144,6 +164,20 @@ class TransitiveClosureBuilder(initialSet: Seq[TopLevel])(implicit microVM: Micr
gcr
}
def maybeGetGlobalCellRec(iref: Word): Option[GlobalCellRec] = {
if (microVM.memoryManager.globalMemory.isInSpace(iref)) {
for ((begin, gcr) <- globalCellMap.to(iref).lastOption) yield {
assert(begin <= iref && iref < gcr.end,
"Address %d 0x%x is in the global space, but the previous global cell's address range is %d 0x%x - %d 0x%x".format(
iref, iref, begin, begin, gcr.end, gcr.end))
gcr
}
} else {
None
}
}
def doTransitiveClosure(): Unit = {
while (tls.hasPending || allocs.hasPending) {
while (tls.hasPending) {
......@@ -350,6 +384,12 @@ class TransitiveClosureBuilder(initialSet: Seq[TopLevel])(implicit microVM: Micr
toFunc.foreach(tls+=)
}
override def visitUPtrField(objRef: Word, iRef: Word, toAddr: Word): Unit = {
maybeGetGlobalCellRec(toAddr).foreach{ gcr =>
tls += gcr.g
}
}
override def visitUFPField(objRef: Word, iRef: Word, toAddr: Word): Unit = {}
override def visitStackRefField(objRef: Word, iRef: Word, toStack: Option[InterpreterStack]): Unit =
toStack.foreach(o => throw noReach("stack", objRef, iRef, o))
override def visitThreadRefField(objRef: Word, iRef: Word, toThread: Option[InterpreterThread]): Unit =
......
package uvm.refimpl.cmdline
import uvm.refimpl.WORD_SIZE_BYTES
import uvm.refimpl.nat.NativeSupport
class NativeArgv(args: Seq[String]) {
val argc = args.size
private val argvSB = new StringBuilder()
private val argvOffs = new Array[Long](argc)
for ((arg, i) <- (args).zipWithIndex) {
argvOffs(i) = argvSB.length
argvSB ++= arg += '\0'
}
// These two buffers are allocated natively and are freed by their finalizers. Keep references to them while using.
val cArgv = NativeSupport.jnrMemoryManager.allocateDirect(argvOffs.length * WORD_SIZE_BYTES.toInt)
val cArgvBuf = NativeSupport.jnrMemoryManager.allocateDirect(argvSB.length)
for (i <- 0 until argvOffs.length) {
cArgv.putAddress(i * WORD_SIZE_BYTES, cArgvBuf.address() + argvOffs(i))
}
for (i <- 0 until argvSB.length) {
cArgvBuf.putByte(i, argvSB.charAt(i).toByte) // Assume single-byte encoding
}
val argv = cArgv.address()
}
\ No newline at end of file
......@@ -67,7 +67,7 @@ object RunMu {
props("bootImg") = bootImg
val microVM = new MicroVM(VMConf(props.toMap))
val microVM = MicroVM(VMConf(props.toMap))
val entryPoint: Either[MuID, MuName] = {
tryWithResource(new ZipFile(bootImg)) { zipFile =>
......@@ -86,24 +86,11 @@ object RunMu {
}
}
}
val argc = appArgs.size + 1
val argvSB = new StringBuilder()