예제 #1
0
static void
SpecializeToMemory(CodeSegment& cs, const Metadata& metadata, HandleWasmMemoryObject memory)
{
    for (const BoundsCheck& check : metadata.boundsChecks)
        Assembler::UpdateBoundsCheck(check.patchAt(cs.code()), memory->buffer().byteLength());

#if defined(JS_CODEGEN_X86)
    uint8_t* base = memory->buffer().dataPointerEither().unwrap();
    for (const MemoryAccess& access : metadata.memoryAccesses) {
        // Patch memory pointer immediate.
        void* addr = access.patchMemoryPtrImmAt(cs.code());
        uint32_t disp = reinterpret_cast<uint32_t>(X86Encoding::GetPointer(addr));
        MOZ_ASSERT(disp <= INT32_MAX);
        X86Encoding::SetPointer(addr, (void*)(base + disp));
    }
#endif
}
예제 #2
0
/* static */ UniqueCodeSegment
CodeSegment::create(JSContext* cx,
                    const Bytes& bytecode,
                    const LinkData& linkData,
                    const Metadata& metadata,
                    HandleWasmMemoryObject memory)
{
    MOZ_ASSERT(bytecode.length() % gc::SystemPageSize() == 0);
    MOZ_ASSERT(linkData.globalDataLength % gc::SystemPageSize() == 0);
    MOZ_ASSERT(linkData.functionCodeLength < bytecode.length());

    auto cs = cx->make_unique<CodeSegment>();
    if (!cs)
        return nullptr;

    cs->bytes_ = AllocateCodeSegment(cx, bytecode.length() + linkData.globalDataLength);
    if (!cs->bytes_)
        return nullptr;

    uint8_t* codeBase = cs->base();

    cs->functionCodeLength_ = linkData.functionCodeLength;
    cs->codeLength_ = bytecode.length();
    cs->globalDataLength_ = linkData.globalDataLength;
    cs->interruptCode_ = codeBase + linkData.interruptOffset;
    cs->outOfBoundsCode_ = codeBase + linkData.outOfBoundsOffset;
    cs->unalignedAccessCode_ = codeBase + linkData.unalignedAccessOffset;

    {
        JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
        AutoFlushICache afc("CodeSegment::create");
        AutoFlushICache::setRange(uintptr_t(codeBase), cs->codeLength());

        memcpy(codeBase, bytecode.begin(), bytecode.length());
        StaticallyLink(*cs, linkData, cx);
        if (memory)
            SpecializeToMemory(nullptr, *cs, metadata, memory->buffer());
    }

    if (!ExecutableAllocator::makeExecutable(codeBase, cs->codeLength())) {
        ReportOutOfMemory(cx);
        return nullptr;
    }

    if (!SendCodeRangesToProfiler(cx, *cs, bytecode, metadata))
        return nullptr;

    return cs;
}
예제 #3
0
static bool
CreateExportObject(JSContext* cx,
                   HandleWasmInstanceObject instanceObj,
                   MutableHandleWasmTableObject tableObj,
                   HandleWasmMemoryObject memoryObj,
                   const ValVector& globalImports,
                   const ExportVector& exports,
                   MutableHandleObject exportObj)
{
    const Instance& instance = instanceObj->instance();
    const Metadata& metadata = instance.metadata();

    if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) {
        RootedFunction fun(cx);
        if (!instanceObj->getExportedFunction(cx, instanceObj, exports[0].funcIndex(), &fun))
            return false;
        exportObj.set(fun);
        return true;
    }

    exportObj.set(JS_NewPlainObject(cx));
    if (!exportObj)
        return false;

    for (const Export& exp : exports) {
        JSAtom* atom = AtomizeUTF8Chars(cx, exp.fieldName(), strlen(exp.fieldName()));
        if (!atom)
            return false;

        RootedId id(cx, AtomToId(atom));
        RootedValue val(cx);
        switch (exp.kind()) {
          case DefinitionKind::Function: {
            RootedFunction fun(cx);
            if (!instanceObj->getExportedFunction(cx, instanceObj, exp.funcIndex(), &fun))
                return false;
            val = ObjectValue(*fun);
            break;
          }
          case DefinitionKind::Table: {
            if (!tableObj) {
                MOZ_ASSERT(instance.tables().length() == 1);
                tableObj.set(WasmTableObject::create(cx, *instance.tables()[0]));
                if (!tableObj)
                    return false;
            }
            val = ObjectValue(*tableObj);
            break;
          }
          case DefinitionKind::Memory: {
            if (metadata.assumptions.newFormat)
                val = ObjectValue(*memoryObj);
            else
                val = ObjectValue(memoryObj->buffer());
            break;
          }
          case DefinitionKind::Global: {
            if (!ExportGlobalValue(cx, metadata.globals, exp.globalIndex(), globalImports, &val))
                return false;
            break;
          }
        }

        if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
            return false;
    }

    return true;
}
예제 #4
0
Instance::Instance(JSContext* cx,
                   Handle<WasmInstanceObject*> object,
                   UniqueCode code,
                   HandleWasmMemoryObject memory,
                   SharedTableVector&& tables,
                   Handle<FunctionVector> funcImports,
                   const ValVector& globalImports)
  : compartment_(cx->compartment()),
    object_(object),
    code_(Move(code)),
    memory_(memory),
    tables_(Move(tables))
{
    MOZ_ASSERT(funcImports.length() == metadata().funcImports.length());
    MOZ_ASSERT(tables_.length() == metadata().tables.length());

    tlsData_.cx = cx;
    tlsData_.instance = this;
    tlsData_.globalData = code_->segment().globalData();
    tlsData_.memoryBase = memory ? memory->buffer().dataPointerEither().unwrap() : nullptr;
    tlsData_.stackLimit = *(void**)cx->stackLimitAddressForJitCode(StackForUntrustedScript);

    for (size_t i = 0; i < metadata().funcImports.length(); i++) {
        HandleFunction f = funcImports[i];
        const FuncImport& fi = metadata().funcImports[i];
        FuncImportTls& import = funcImportTls(fi);
        if (IsExportedFunction(f) && !isAsmJS() && !ExportedFunctionToInstance(f).isAsmJS()) {
            Instance& calleeInstance = ExportedFunctionToInstance(f);
            const Metadata& calleeMetadata = calleeInstance.metadata();
            uint32_t funcIndex = ExportedFunctionToIndex(f);
            const FuncExport& funcExport = calleeMetadata.lookupFuncExport(funcIndex);
            const CodeRange& codeRange = calleeMetadata.codeRanges[funcExport.codeRangeIndex()];
            import.tls = &calleeInstance.tlsData_;
            import.code = calleeInstance.codeSegment().base() + codeRange.funcNonProfilingEntry();
            import.baselineScript = nullptr;
            import.obj = ExportedFunctionToInstanceObject(f);
        } else {
            import.tls = &tlsData_;
            import.code = codeBase() + fi.interpExitCodeOffset();
            import.baselineScript = nullptr;
            import.obj = f;
        }
    }

    uint8_t* globalData = code_->segment().globalData();

    for (size_t i = 0; i < metadata().globals.length(); i++) {
        const GlobalDesc& global = metadata().globals[i];
        if (global.isConstant())
            continue;

        uint8_t* globalAddr = globalData + global.offset();
        switch (global.kind()) {
          case GlobalKind::Import: {
            globalImports[global.importIndex()].writePayload(globalAddr);
            break;
          }
          case GlobalKind::Variable: {
            const InitExpr& init = global.initExpr();
            switch (init.kind()) {
              case InitExpr::Kind::Constant: {
                init.val().writePayload(globalAddr);
                break;
              }
              case InitExpr::Kind::GetGlobal: {
                const GlobalDesc& imported = metadata().globals[init.globalIndex()];
                globalImports[imported.importIndex()].writePayload(globalAddr);
                break;
              }
            }
            break;
          }
          case GlobalKind::Constant: {
            MOZ_CRASH("skipped at the top");
          }
        }
    }

    for (size_t i = 0; i < tables_.length(); i++)
        *addressOfTableBase(i) = tables_[i]->base();
}
예제 #5
0
static bool
CreateExportObject(JSContext* cx,
                   HandleWasmInstanceObject instanceObj,
                   HandleWasmMemoryObject memoryObj,
                   const ExportVector& exports,
                   MutableHandleObject exportObj)
{
    const Instance& instance = instanceObj->instance();
    const Metadata& metadata = instance.metadata();
    const SharedTableVector& tables = instance.tables();

    if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) {
        exportObj.set(NewExportedFunction(cx, instanceObj, 0));
        return !!exportObj;
    }

    exportObj.set(JS_NewPlainObject(cx));
    if (!exportObj)
        return false;

    Rooted<ValueVector> vals(cx, ValueVector(cx));
    for (size_t i = 0; i < metadata.funcExports.length(); i++) {
        JSFunction* fun = NewExportedFunction(cx, instanceObj, i);
        if (!fun || !vals.append(ObjectValue(*fun)))
            return false;
    }

    RootedWasmTableObject tableObj(cx);
    for (const Export& exp : exports) {
        JSAtom* atom = AtomizeUTF8Chars(cx, exp.fieldName(), strlen(exp.fieldName()));
        if (!atom)
            return false;

        RootedId id(cx, AtomToId(atom));
        RootedValue val(cx);
        switch (exp.kind()) {
          case DefinitionKind::Function:
            val = vals[exp.funcExportIndex()];
            break;
          case DefinitionKind::Table:
            MOZ_ASSERT(tables.length() == 1);
            if (!tableObj) {
                tableObj = WasmTableObject::create(cx, *tables[0]);
                if (!tableObj)
                    return false;
            }
            val = ObjectValue(*tableObj);
            break;
          case DefinitionKind::Memory:
            if (metadata.assumptions.newFormat)
                val = ObjectValue(*memoryObj);
            else
                val = ObjectValue(memoryObj->buffer());
            break;
        }

        if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
            return false;
    }

    return true;
}
예제 #6
0
bool
Module::initSegments(JSContext* cx,
                     HandleWasmInstanceObject instanceObj,
                     Handle<FunctionVector> funcImports,
                     HandleWasmMemoryObject memoryObj,
                     const ValVector& globalImports) const
{
    Instance& instance = instanceObj->instance();
    const SharedTableVector& tables = instance.tables();

    // Perform all error checks up front so that this function does not perform
    // partial initialization if an error is reported.

    for (const ElemSegment& seg : elemSegments_) {
        uint32_t numElems = seg.elemCodeRangeIndices.length();
        if (!numElems)
            continue;

        uint32_t tableLength = tables[seg.tableIndex]->length();
        uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);

        if (offset > tableLength || tableLength - offset < numElems) {
            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT,
                                      "elem", "table");
            return false;
        }
    }

    if (memoryObj) {
        for (const DataSegment& seg : dataSegments_) {
            if (!seg.length)
                continue;

            uint32_t memoryLength = memoryObj->buffer().byteLength();
            uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);

            if (offset > memoryLength || memoryLength - offset < seg.length) {
                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT,
                                          "data", "memory");
                return false;
            }
        }
    } else {
        MOZ_ASSERT(dataSegments_.empty());
    }

    // Now that initialization can't fail partway through, write data/elem
    // segments into memories/tables.

    for (const ElemSegment& seg : elemSegments_) {
        Table& table = *tables[seg.tableIndex];
        uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
        bool profilingEnabled = instance.code().profilingEnabled();
        const CodeRangeVector& codeRanges = metadata().codeRanges;
        uint8_t* codeBase = instance.codeBase();

        for (uint32_t i = 0; i < seg.elemCodeRangeIndices.length(); i++) {
            uint32_t funcIndex = seg.elemFuncIndices[i];
            if (funcIndex < funcImports.length() && IsExportedWasmFunction(funcImports[funcIndex])) {
                MOZ_ASSERT(!metadata().isAsmJS());
                MOZ_ASSERT(!table.isTypedFunction());

                HandleFunction f = funcImports[funcIndex];
                WasmInstanceObject* exportInstanceObj = ExportedFunctionToInstanceObject(f);
                const CodeRange& cr = exportInstanceObj->getExportedFunctionCodeRange(f);
                Instance& exportInstance = exportInstanceObj->instance();
                table.set(offset + i, exportInstance.codeBase() + cr.funcTableEntry(), exportInstance);
            } else {
                const CodeRange& cr = codeRanges[seg.elemCodeRangeIndices[i]];
                uint32_t entryOffset = table.isTypedFunction()
                                       ? profilingEnabled
                                         ? cr.funcProfilingEntry()
                                         : cr.funcNonProfilingEntry()
                                       : cr.funcTableEntry();
                table.set(offset + i, codeBase + entryOffset, instance);
            }
        }
    }

    if (memoryObj) {
        uint8_t* memoryBase = memoryObj->buffer().dataPointerEither().unwrap(/* memcpy */);

        for (const DataSegment& seg : dataSegments_) {
            MOZ_ASSERT(seg.bytecodeOffset <= bytecode_->length());
            MOZ_ASSERT(seg.length <= bytecode_->length() - seg.bytecodeOffset);
            uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
            memcpy(memoryBase + offset, bytecode_->begin() + seg.bytecodeOffset, seg.length);
        }
    }

    return true;
}