Exemplo n.º 1
0
        unsafe public static Module LoadBinary(byte *pb, ref uint idx, int endIdx)
        {
            // https://www.reddit.com/r/WebAssembly/comments/9vq019/is_anyone_learning_webassembly_in_binary_im_stuck/
            // https://webassembly.github.io/wabt/demo/wat2wasm/

            if (*((int *)(&pb[idx])) != BinParse.WASM_BINARY_MAGIC)
            {
                return(null);
            }

            idx += 4;

            if (*(int *)&pb[idx] != BinParse.WASM_BINARY_VERSION)
            {
                return(null);
            }

            idx += 4;

            Module ret = new Module();

            while (idx < endIdx)
            {
                Bin.Section sectionCode = (Bin.Section)pb[idx];
                ++idx;

                uint sectionSize = BinParse.LoadUnsignedLEB32(pb, ref idx);

                if (sectionCode == Bin.Section.CustomSec)
                {
                    uint end = idx + sectionSize;

                    while (true)
                    {
                        return(ret);

                        // TODO: Considered end and unprocessed for now.
                        //
                        // uint nameLen = LoadUnsignedLEB32(pb, ref idx);
                        // string customSecName = LoadString(pb, nameLen, ref idx);
                        //
                        // uint subType = LoadUnsignedLEB32(pb, ref idx);
                        // uint subSize = LoadUnsignedLEB32(pb, ref idx);
                    }
                }
                else if (sectionCode == Bin.Section.TypeSec)
                {
                    uint numTypes = BinParse.LoadUnsignedLEB32(pb, ref idx);
                    for (uint i = 0; i < numTypes; ++i)
                    {
                        Bin.TypeID type = (Bin.TypeID)BinParse.LoadUnsignedLEB32(pb, ref idx);
                        if (type == Bin.TypeID.Function)
                        {
                            FunctionType fty = new FunctionType();
                            fty.typeid = (uint)type;
                            ret.types.Add(fty);

                            uint numParams = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            for (uint j = 0; j < numParams; ++j)
                            {
                                FunctionType.DataOrgInfo paramInfo = new FunctionType.DataOrgInfo();
                                paramInfo.type = (Bin.TypeID)BinParse.LoadUnsignedLEB32(pb, ref idx);
                                fty.paramTypes.Add(paramInfo);
                            }

                            uint numResults = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            for (uint j = 0; j < numResults; ++j)
                            {
                                FunctionType.DataOrgInfo resultInfo = new FunctionType.DataOrgInfo();
                                resultInfo.type = (Bin.TypeID)BinParse.LoadUnsignedLEB32(pb, ref idx);
                                fty.resultTypes.Add(resultInfo);
                            }

                            fty.InitializeOrganization();
                        }
                        else
                        {
                        }
                    }
                }
                else if (sectionCode == Bin.Section.ImportSec)
                {
                    uint numImports = BinParse.LoadUnsignedLEB32(pb, ref idx);

                    for (uint i = 0; i < numImports; ++i)
                    {
                        uint   modnameLen = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        string modName    = LoadString(pb, modnameLen, ref idx);

                        uint   fieldnameLen = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        string fieldName    = LoadString(pb, fieldnameLen, ref idx);

                        ImportType importTy = (ImportType)BinParse.LoadUnsignedLEB32(pb, ref idx);
                        switch (importTy)
                        {
                        case ImportType.TypeIndex:
                        {
                            uint         fnTyIdx = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            FunctionType fnTy    = ret.types[(int)fnTyIdx];
                            ret.storeDecl.AddFunctionImp(modName, fieldName, fnTy);
                        }
                        break;

                        case ImportType.TableType:
                        {
                            const int FLAG_HASMAX = 0x01;
                            const int FLAG_OTHERS = ~(FLAG_HASMAX);

                            // We may be able top unify parts of this code with non-imported tables
                            uint type    = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            uint flags   = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            uint initial = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            uint?max     = null;

                            if ((flags & FLAG_HASMAX) != 0)
                            {
                                max = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            }

                            if ((flags & FLAG_OTHERS) != 0)
                            {
                                throw new System.Exception("Encountered unknown flags for imported table.");
                            }

                            ret.storeDecl.AddTableImp(modName, fieldName, (Bin.TypeID)type, initial, max);
                        }
                        break;

                        case ImportType.MemType:
                        {
                            const int FLAG_HASMAX = 0x01;
                            const int FLAG_OTHERS = ~(FLAG_HASMAX);

                            // We may be able to unify parts of this code with non-imported memory
                            uint flags   = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            uint initial = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            uint?max     = null;

                            if ((flags & FLAG_HASMAX) != 0)
                            {
                                max = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            }

                            if ((flags & FLAG_OTHERS) != 0)
                            {
                                throw new System.Exception("Encountered unknown flags for imported memory.");
                            }

                            ret.storeDecl.AddMemoryImp(modName, fieldName, initial, initial, max);
                        }
                        break;

                        case ImportType.GlobalType:
                        {
                            uint type       = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            uint mutability = BinParse.LoadUnsignedLEB32(pb, ref idx);

                            ret.storeDecl.AddGlobalImp(modName, fieldName, (Bin.TypeID)type, mutability != 0);
                        }
                        break;
                        }
                    }
                }
                else if (sectionCode == Bin.Section.FunctionSec)
                {
                    uint numFunctions = BinParse.LoadUnsignedLEB32(pb, ref idx);
                    for (uint i = 0; i < numFunctions; ++i)
                    {
                        Function function = new Function(ret);
                        uint     fnType   = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        function.typeidx = fnType;
                        function.fnType  = ret.types[(int)fnType];

                        ret.storeDecl.AddFunctionLoc(function.fnType);

                        ret.functions.Add(function);
                    }
                }
                else if (sectionCode == Bin.Section.TableSec)
                {
                    uint numTables = BinParse.LoadUnsignedLEB32(pb, ref idx);

                    for (uint i = 0; i < numTables; ++i)
                    {
                        const int FLAG_HASMAX = 0x01;
                        const int FLAG_OTHERS = ~(FLAG_HASMAX);

                        Bin.TypeID ty = (Bin.TypeID)BinParse.LoadUnsignedLEB32(pb, ref idx);

                        uint flags   = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        uint initial = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        uint?max     = null;

                        if ((flags & FLAG_HASMAX) != 0)
                        {
                            max = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        }

                        if ((flags & FLAG_OTHERS) != 0)
                        {
                            throw new System.Exception("Table section contains unsupported flags.");
                        }

                        ret.storeDecl.AddTableLoc(ty, initial, max);
                    }
                }
                else if (sectionCode == Bin.Section.MemorySec)
                {
                    // Prepare the declaration of memory regions.
                    //
                    // Note that this is only prepping for the data payloads, actual
                    // parsing of that data happens in the Data section.
                    uint numMems = BinParse.LoadUnsignedLEB32(pb, ref idx); // Right now this is assumed to be 1

                    for (uint i = 0; i < numMems; ++i)
                    {
                        const int FLAG_HASMAX = 0x01;
                        const int FLAG_OTHERS = ~(FLAG_HASMAX);

                        uint memFlags         = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        uint memInitialPageCt = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        uint?memMaxPageCt     = null;

                        if ((memFlags & FLAG_HASMAX) != 0)
                        {
                            memMaxPageCt = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        }

                        if ((memFlags & FLAG_OTHERS) != 0)
                        {
                            throw new System.Exception("Memory section contains unsupported flags.");
                        }

                        ret.storeDecl.AddMemoryLoc(memInitialPageCt, memInitialPageCt, memMaxPageCt);
                    }
                }
                else if (sectionCode == Bin.Section.GlobalSec)
                {
                    uint numGlobals = BinParse.LoadUnsignedLEB32(pb, ref idx);
                    for (uint i = 0; i < numGlobals; ++i)
                    {
                        const int FLAG_MUTABLE = 0x01;
                        const int FLAG_OTHERS  = ~(FLAG_MUTABLE);

                        uint globType  = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        uint globFlags = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        bool mutable   = (globFlags & FLAG_MUTABLE) != 0;

                        if ((globFlags & FLAG_OTHERS) != 0)
                        {
                            throw new System.Exception("Global section contains unsupported flags.");
                        }

                        // For now we're just going to assume they do a type.const, then the value,
                        // and then an end.
                        //
                        // I actually haven't read the specs to see what's allowed here.
                        if (globType == (int)Bin.TypeID.Int32)
                        {
                            AssertConsumeByte(pb, ref idx, (byte)Instruction.i32_const);
                            int idef = BinParse.LoadSignedLEB32(pb, ref idx);
                            AssertConsumeByte(pb, ref idx, (byte)Instruction.end);

                            ret.storeDecl.AddGlobalLoc(idef, mutable);
                        }
                        else if (globType == (int)Bin.TypeID.Float32)
                        {
                            AssertConsumeByte(pb, ref idx, (byte)Instruction.f32_const);
                            float fdef = *(float *)&pb[idx];
                            idx += 4;
                            AssertConsumeByte(pb, ref idx, (byte)Instruction.end);

                            ret.storeDecl.AddGlobalLoc(fdef, mutable);
                        }
                        else if (globType == (int)Bin.TypeID.Int64)
                        {
                            AssertConsumeByte(pb, ref idx, (byte)Instruction.i64_const);
                            long ldef = BinParse.LoadSignedLEB64(pb, ref idx);
                            AssertConsumeByte(pb, ref idx, (byte)Instruction.end);

                            ret.storeDecl.AddGlobalLoc(ldef, mutable);
                        }
                        else if (globType == (int)Bin.TypeID.Float64)
                        {
                            AssertConsumeByte(pb, ref idx, (byte)Instruction.f64_const);
                            double ddef = *(float *)&pb[idx];
                            idx += 8;
                            AssertConsumeByte(pb, ref idx, (byte)Instruction.end);

                            ret.storeDecl.AddGlobalLoc(ddef, mutable);
                        }
                        else
                        {
                            throw new System.Exception("Unexpected global type.");
                        }
                    }
                }
                else if (sectionCode == Bin.Section.ExportSec)
                {
                    uint numExports = BinParse.LoadUnsignedLEB32(pb, ref idx);
                    for (uint i = 0; i < numExports; ++i)
                    {
                        uint   strLen = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        string name   = LoadString(pb, strLen, ref idx);
                        uint   kind   = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        uint   index  = BinParse.LoadUnsignedLEB32(pb, ref idx);

                        Export export = new Export();
                        export.name  = name;
                        export.kind  = (ImportType)kind;
                        export.index = index;
                        ret.exports.Add(export);
                    }
                }
                else if (sectionCode == Bin.Section.StartSec)
                {
                    ret.startFnIndex = BinParse.LoadUnsignedLEB32(pb, ref idx);
                    ret.ValidateStartFunction(true);
                }
                else if (sectionCode == Bin.Section.ElementSec)
                {
                    uint numSegments = BinParse.LoadUnsignedLEB32(pb, ref idx);

                    if (numSegments == 0)
                    {
                        continue;
                    }

                    if (ret.storeDecl.tables.Count < 1)
                    {
                        throw new System.Exception("Element(s) specified when no tables are defined.");
                    }

                    DefTable defTable = ret.storeDecl.tables[0];

                    for (uint i = 0; i < numSegments; ++i)
                    {
                        // Table index
                        uint flags = BinParse.LoadUnsignedLEB32(pb, ref idx);

                        DefSegment ds = new DefSegment(pb, ref idx, false);

                        Bin.TypeID tabTy  = ret.storeDecl.tables[0].type;
                        uint       tySize = DataStore.GetTypeIDSize(tabTy);

                        uint elemCt = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        ds.data = new byte[elemCt * tySize];

                        fixed(byte *ptabdefs = ds.data)
                        {
                            switch (tabTy)
                            {
                            case Bin.TypeID.FuncRef:
                            case Bin.TypeID.Int32:
                                for (int j = 0; j < elemCt; ++j)
                                {
                                    ((int *)ptabdefs)[j] = BinParse.LoadSignedLEB32(pb, ref idx);
                                }
                                break;

                            case Bin.TypeID.Float32:
                                for (int j = 0; j < elemCt; ++j)
                                {
                                    ((float *)ptabdefs)[j] = *(float *)pb;
                                    idx += 4;
                                }
                                break;

                            case Bin.TypeID.Int64:
                                for (int j = 0; j < elemCt; ++j)
                                {
                                    ((long *)ptabdefs)[j] = BinParse.LoadSignedLEB64(pb, ref idx);
                                }
                                break;

                            case Bin.TypeID.Float64:
                                for (int j = 0; j < elemCt; ++j)
                                {
                                    ((double *)ptabdefs)[j] = *(double *)pb;
                                    idx += 8;
                                }
                                break;
                            }
                        }
                    }
                }
                else if (sectionCode == Bin.Section.CodeSec)
                {
                    uint numFunctions = BinParse.LoadUnsignedLEB32(pb, ref idx);
                    for (uint i = 0; i < numFunctions; ++i)
                    {
                        Function function = ret.functions[(int)i];

                        uint bodySize = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        uint end      = idx + bodySize;

                        uint localsCount = BinParse.LoadUnsignedLEB32(pb, ref idx);
                        for (int j = 0; j < localsCount; ++j)
                        {
                            // The number of consecutive occurences of this type
                            uint localTyCt = BinParse.LoadUnsignedLEB32(pb, ref idx);
                            // The type to place on the stack. The quantity of how many
                            // is specified in localTyCt.
                            uint type = BinParse.LoadUnsignedLEB32(pb, ref idx);

                            for (int k = 0; k < localTyCt; ++k)
                            {
                                FunctionType.DataOrgInfo doi = new FunctionType.DataOrgInfo();
                                doi.type = (Bin.TypeID)type;
                                function.localTypes.Add(doi);
                            }
                        }
                        function.InitializeOrganization();

                        uint size = end - idx;
                        function.expression = new byte[size];

                        System.Runtime.InteropServices.Marshal.Copy(
                            (System.IntPtr)(int *)(&pb[idx]),
                            function.expression,
                            (int)0,
                            (int)size);

                        idx = end;
                    }

                    for (uint i = 0; i < numFunctions; ++i)
                    {
                        ret.functions[(int)i].ExpandExpressionToBeUsable(ret);
                    }
                }
                else if (sectionCode == Bin.Section.DataSec)
                {
                    uint numData = BinParse.LoadUnsignedLEB32(pb, ref idx);

                    if (numData == 0)
                    {
                        continue;
                    }

                    if (ret.storeDecl.memories.Count < 1)
                    {
                        throw new System.Exception("Data(s) specified when no mems are defined.");
                    }

                    for (uint i = 0; i < numData; ++i)
                    {
                        uint segHeaderFlags = BinParse.LoadUnsignedLEB32(pb, ref idx);

                        DefMem dmem = ret.storeDecl.memories[0];

                        DefSegment ds     = new DefSegment(pb, ref idx, false);
                        uint       dataSz = BinParse.LoadUnsignedLEB32(pb, ref idx);

                        ds.data = new byte[dataSz];

                        // We're going to do the copy manually, but if there's a C#
                        // low-level copy function that also does this, that would be
                        // prefered.
                        for (uint j = 0; j < dataSz; ++j)
                        {
                            ds.data[j] = pb[idx + j];
                        }

                        dmem.AddDefault(ds);

                        //ret.storeDecl.memories[(int)i] = dmem;
                        ret.storeDecl.memories[0] = dmem;

                        idx += dataSz;
                    }
                }
                else
                {
                    throw new System.Exception("Encountered unknown Module section.");
                }
            }
            ++idx;

            return(ret);
        }
Exemplo n.º 2
0
 public void AddDefault(DefSegment ds)
 {
     this.defSegments.Add(ds);
 }