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

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

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 = {
......
......@@ -33,63 +33,30 @@ import uvm.utils.IOHelpers
import uvm.utils.WithUtils.tryWithResource
class BootImageBuilder(implicit microVM: MicroVM) {
def makeBootImage(whiteList: Seq[TopLevel], outputFile: String): Unit = {
implicit val tcb = new TransitiveClosureBuilder(whiteList)
def makeBootImage(whiteList: Seq[TopLevel], primordial: PrimordialInfo,
syms: Seq[FieldAndSymbol], relocs: Seq[FieldAndSymbol], outputFile: String): Unit = {
implicit val tcb = new TransitiveClosureBuilder(whiteList, primordial)
tcb.doTransitiveClosure()
val biw = new BootImageWriter(tcb, outputFile)
val biw = new BootImageWriter(tcb, syms, relocs, outputFile)
biw.writeFile()
}
}
case class PrimordialInfo(maybeFunc: Option[Function], maybeStack: Option[InterpreterStack], maybeThreadLocal: Option[Word])
object PrimordialInfo {
val NO_PRIMORDIAL = PrimordialInfo(None, None, None)
}
case class FieldAndSymbol(objRef: Word, offset: Word, symbol: String) {
def toAddrSymPair: (Word, String) = ((objRef + offset), symbol)
}
object BootImageWriter {
import BootImageFile._
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
val FILEEXT_DATA = ".data"
val FILEEXT_ALLOC = ".alloc"
val FILEEXT_RELOC = ".reloc"
val UIRBUNDLE_FILE = "bundle.uir"
val IDNAMEMAP_FILE = "idnamemap"
val AS_REF = "R"
val AS_IREF = "I"
val AS_TAGREF = "T"
val AS_FUNCREF = "F"
val TO_GLOBAL = "G"
val TO_HEAP = "H"
val TO_FUNC = "F"
val NO_OFFSET = 0L
/**
* 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)
class FileGroup(baseName: String, dir: Path) {
val dataFile = dir.resolve(baseName + FILEEXT_DATA)
val allocFile = dir.resolve(baseName + FILEEXT_ALLOC)
......@@ -102,8 +69,10 @@ object BootImageWriter {
}
}
class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implicit microVM: MicroVM) {
class BootImageWriter(tcb: TransitiveClosureBuilder, syms: Seq[FieldAndSymbol],
relocs: Seq[FieldAndSymbol], outputFile: String)(implicit microVM: MicroVM) {
import BootImageWriter._
import BootImageFile._
val bundleSerializer = new BundleSerializer(microVM.globalBundle, tcb.tls.set)
......@@ -113,13 +82,22 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
val globalGroup = new FileGroup("global", tempDir)
val heapGroup = new FileGroup("heap", tempDir)
val symsMap = syms.map(_.toAddrSymPair).toMap
val symsRevMap = syms.map(_.toAddrSymPair.swap).toMap
val relocsMap = relocs.map(_.toAddrSymPair).toMap
{
logger.debug("Relocation map: " + relocsMap.toString)
}
val idNameMapPath = tempDir.resolve(IDNAMEMAP_FILE)
val uirBundlePath = tempDir.resolve(UIRBUNDLE_FILE)
val metaInfoPath = tempDir.resolve(METAINFO_FILE)
val zipPath = Paths.get(outputFile)
val allZipContents = Seq(globalGroup.dataFile, heapGroup.dataFile, globalGroup.allocFile, heapGroup.allocFile,
globalGroup.relocFile, heapGroup.relocFile, idNameMapPath, uirBundlePath)
globalGroup.relocFile, heapGroup.relocFile, idNameMapPath, uirBundlePath, metaInfoPath)
private val globalMemory: Space = microVM.memoryManager.globalMemory
private val smallObjectSpace: Space = microVM.memoryManager.heap.space
......@@ -149,6 +127,8 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
scanForRelocs(globalGroup)
scanForRelocs(heapGroup)
addPointerRelocs()
writeRelocRecs(globalGroup)
writeRelocRecs(heapGroup)
......@@ -156,6 +136,8 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
writeIDNameMap()
writeUIRBundle()
writeMetaInfo()
makeZipPackage()
}
......@@ -200,17 +182,53 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
val begin = alloc.addr
MemoryDataScanner.scanAllocUnit(begin, new MemoryFieldHandler {
def visitUPtrField(objRef: Word, iRef: Word, toAddr: Word): Unit = {
relocsMap.get(iRef) match {
case Some(symbol) => {
// by symbol
val fieldOffset = iRef - objRef
val reloc = FieldRelocRecord(num, fieldOffset, AS_UPTR, TO_SYM, 0, 0, symbol)
group.relocRecs += reloc
logger.debug("External uptr relocation: %d 0x%x -> %s".format(iRef, iRef, symbol))
}
case None => {
// maybe a "reasonable" pointer to a mu global cell. Relocate it too.
val fieldOffset = iRef - objRef
val maybeGlobalCell = tcb.maybeGetGlobalCellRec(toAddr)
maybeGlobalCell match {
case Some(gcr) =>
val targetNum = gcr.g.id.toLong
val targetAddr = gcr.begin
val targetOffset = toAddr - targetAddr
val reloc = FieldRelocRecord(num, fieldOffset, AS_UPTR, TO_GLOBAL, targetNum, targetOffset, NO_SYM)
group.relocRecs += reloc
logger.info("Relocation entry for uptr field automatically generated: %d 0x%x -> %d 0x%x".format(
iRef, iRef, toAddr, toAddr))
case None => }
}
}
}
def visitUFPField(objRef: Word, iRef: Word, toAddr: Word): Unit = {
relocsMap.get(iRef).foreach { symbol =>
// by symbol
val fieldOffset = iRef - objRef
val reloc = FieldRelocRecord(num, fieldOffset, AS_UPTR, TO_SYM, 0, 0, symbol)
group.relocRecs += reloc
logger.debug("External ufuncptr relocation: %d 0x%x -> %s".format(iRef, iRef, symbol))
}
}
def visitRefField(objRef: Word, iRef: Word, toObj: Word, isWeak: Boolean): Unit = if (toObj != 0L) {
val fieldOffset = iRef - objRef
val targetNum = addrToHeapObjNum(toObj)
val reloc = FieldRelocRecord(num, fieldOffset, AS_REF, TO_HEAP, targetNum, NO_OFFSET)
val reloc = FieldRelocRecord(num, fieldOffset, AS_REF, TO_HEAP, targetNum, NO_OFFSET, NO_SYM)
group.relocRecs += reloc
}
def visitIRefField(objRef: Word, iRef: Word, toObj: Word, toOffset: Word): Unit = if (toObj != 0L) {
val fieldOffset = iRef - objRef
val targetNum = addrToHeapObjNum(toObj)
val reloc = FieldRelocRecord(num, fieldOffset, AS_IREF, TO_HEAP, targetNum, toOffset)
val reloc = FieldRelocRecord(num, fieldOffset, AS_IREF, TO_HEAP, targetNum, toOffset, NO_SYM)
group.relocRecs += reloc
} else {
val fieldOffset = iRef - objRef
......@@ -218,21 +236,21 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
val targetNum = gcr.g.id.toLong
val targetAddr = gcr.begin
val targetOffset = toOffset - targetAddr
val reloc = FieldRelocRecord(num, fieldOffset, AS_IREF, TO_GLOBAL, targetNum, targetOffset)
val reloc = FieldRelocRecord(num, fieldOffset, AS_IREF, TO_GLOBAL, targetNum, targetOffset, NO_SYM)
group.relocRecs += reloc
}
def visitTagRefField(objRef: Word, iRef: Word, toObj: Word): Unit = if (toObj != 0L) {
val fieldOffset = iRef - objRef
val targetNum = addrToHeapObjNum(toObj)
val reloc = FieldRelocRecord(num, fieldOffset, AS_TAGREF, TO_HEAP, targetNum, NO_OFFSET)
val reloc = FieldRelocRecord(num, fieldOffset, AS_TAGREF, TO_HEAP, targetNum, NO_OFFSET, NO_SYM)
group.relocRecs += reloc
}
def visitFuncRefField(objRef: Word, iRef: Word, toFunc: Option[Function]): Unit = toFunc.foreach { func =>
val fieldOffset = iRef - objRef
val targetNum = func.id.toLong
val reloc = FieldRelocRecord(num, fieldOffset, AS_FUNCREF, TO_FUNC, targetNum, NO_OFFSET)
val reloc = FieldRelocRecord(num, fieldOffset, AS_FUNCREF, TO_FUNC, targetNum, NO_OFFSET, NO_SYM)
group.relocRecs += reloc
}
......@@ -244,11 +262,16 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
})
}
}
private def addPointerRelocs(): Unit = {
// Seems uptrs are already handled when scanning fields. No further processing is needed at this moment.
}
private def writeRelocRecs(group: FileGroup): Unit = {
tryWithResource(Files.newBufferedWriter(group.relocFile, StandardCharsets.UTF_8)) { writer =>
for (rr <- group.relocRecs) {
writer.write("%d,%d,%s,%s,%d,%d\n".format(rr.num, rr.fieldOffset, rr.fieldKind, rr.targetKind, rr.targetNum, rr.targetOffset))
writer.write("%d,%d,%s,%s,%d,%d,%s\n".format(
rr.num, rr.fieldOffset, rr.fieldKind, rr.targetKind, rr.targetNum, rr.targetOffset, rr.targetString))
}
}
}
......@@ -264,6 +287,22 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
bundleSerializer.writeUIR(writer)
}
}
private def writeMetaInfo(): Unit = {
tryWithResource(Files.newBufferedWriter(metaInfoPath, StandardCharsets.UTF_8)) { writer =>
val sb = new StringBuilder()
tcb.primordial.maybeFunc.foreach { func =>
sb.append("%s=%d\n".format(KEY_INITFUNC, func.id))
tcb.primordial.maybeThreadLocal.foreach { addr =>
val heapObjNum = heapGroup.allocRecs.find(_.addr == addr).getOrElse {
throw new BootImageBuilderException("BUG: Thread local object is not in the boot image")
}.num
sb.append("%s=%d\n".format(KEY_THREADLOCAL, heapObjNum))
}
}
writer.write(sb.toString)
}
}
private def makeZipPackage(): Unit = {
tryWithResource(new ZipOutputStream(Files.newOutputStream(zipPath))) { zos =>
......
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.