%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/wasm/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/wasm/module-decoder.cc |
// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/wasm/module-decoder.h" #include "src/logging/metrics.h" #include "src/tracing/trace-event.h" #include "src/wasm/constant-expression.h" #include "src/wasm/decoder.h" #include "src/wasm/module-decoder-impl.h" #include "src/wasm/struct-types.h" #include "src/wasm/wasm-constants.h" #include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-limits.h" #include "src/wasm/wasm-opcodes-inl.h" namespace v8 { namespace internal { namespace wasm { const char* SectionName(SectionCode code) { switch (code) { case kUnknownSectionCode: return "Unknown"; case kTypeSectionCode: return "Type"; case kImportSectionCode: return "Import"; case kFunctionSectionCode: return "Function"; case kTableSectionCode: return "Table"; case kMemorySectionCode: return "Memory"; case kGlobalSectionCode: return "Global"; case kExportSectionCode: return "Export"; case kStartSectionCode: return "Start"; case kCodeSectionCode: return "Code"; case kElementSectionCode: return "Element"; case kDataSectionCode: return "Data"; case kTagSectionCode: return "Tag"; case kStringRefSectionCode: return "StringRef"; case kDataCountSectionCode: return "DataCount"; case kNameSectionCode: return kNameString; case kSourceMappingURLSectionCode: return kSourceMappingURLString; case kDebugInfoSectionCode: return kDebugInfoString; case kExternalDebugInfoSectionCode: return kExternalDebugInfoString; case kInstTraceSectionCode: return kInstTraceString; case kCompilationHintsSectionCode: return kCompilationHintsString; case kBranchHintsSectionCode: return kBranchHintsString; default: return "<unknown>"; } } ModuleResult DecodeWasmModule( WasmFeatures enabled_features, base::Vector<const uint8_t> wire_bytes, bool validate_functions, ModuleOrigin origin, Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder, v8::metrics::Recorder::ContextId context_id, DecodingMethod decoding_method) { if (counters) { auto size_counter = SELECT_WASM_COUNTER(counters, origin, wasm, module_size_bytes); static_assert(kV8MaxWasmModuleSize < kMaxInt); size_counter->AddSample(static_cast<int>(wire_bytes.size())); } v8::metrics::WasmModuleDecoded metrics_event; base::ElapsedTimer timer; timer.Start(); ModuleResult result = DecodeWasmModule(enabled_features, wire_bytes, validate_functions, origin); if (counters && result.ok()) { auto counter = SELECT_WASM_COUNTER(counters, origin, wasm_functions_per, module); counter->AddSample( static_cast<int>(result.value()->num_declared_functions)); } // Record event metrics. metrics_event.wall_clock_duration_in_us = timer.Elapsed().InMicroseconds(); timer.Stop(); metrics_event.success = result.ok(); metrics_event.async = decoding_method == DecodingMethod::kAsync || decoding_method == DecodingMethod::kAsyncStream; metrics_event.streamed = decoding_method == DecodingMethod::kSyncStream || decoding_method == DecodingMethod::kAsyncStream; if (result.ok()) { metrics_event.function_count = result.value()->num_declared_functions; } metrics_event.module_size_in_bytes = wire_bytes.size(); metrics_recorder->DelayMainThreadEvent(metrics_event, context_id); return result; } ModuleResult DecodeWasmModule( WasmFeatures enabled_features, base::Vector<const uint8_t> wire_bytes, bool validate_functions, ModuleOrigin origin, PopulateExplicitRecGroups populate_explicit_rec_groups) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"), "wasm.DecodeWasmModule"); ModuleDecoderImpl decoder{enabled_features, wire_bytes, origin, populate_explicit_rec_groups}; return decoder.DecodeModule(validate_functions); } ModuleResult DecodeWasmModuleForDisassembler( base::Vector<const uint8_t> wire_bytes) { constexpr bool kNoValidateFunctions = false; ModuleDecoderImpl decoder{WasmFeatures::All(), wire_bytes, kWasmOrigin}; return decoder.DecodeModule(kNoValidateFunctions); } ModuleDecoder::ModuleDecoder(WasmFeatures enabled_features) : impl_(std::make_unique<ModuleDecoderImpl>( enabled_features, base::Vector<const uint8_t>{}, kWasmOrigin)) {} ModuleDecoder::~ModuleDecoder() = default; const std::shared_ptr<WasmModule>& ModuleDecoder::shared_module() const { return impl_->shared_module(); } void ModuleDecoder::DecodeModuleHeader(base::Vector<const uint8_t> bytes) { impl_->DecodeModuleHeader(bytes); } void ModuleDecoder::DecodeSection(SectionCode section_code, base::Vector<const uint8_t> bytes, uint32_t offset) { impl_->DecodeSection(section_code, bytes, offset); } void ModuleDecoder::DecodeFunctionBody(uint32_t index, uint32_t length, uint32_t offset) { impl_->DecodeFunctionBody(index, length, offset); } void ModuleDecoder::StartCodeSection(WireBytesRef section_bytes) { impl_->StartCodeSection(section_bytes); } bool ModuleDecoder::CheckFunctionsCount(uint32_t functions_count, uint32_t error_offset) { return impl_->CheckFunctionsCount(functions_count, error_offset); } ModuleResult ModuleDecoder::FinishDecoding() { return impl_->FinishDecoding(); } size_t ModuleDecoder::IdentifyUnknownSection(ModuleDecoder* decoder, base::Vector<const uint8_t> bytes, uint32_t offset, SectionCode* result) { if (!decoder->ok()) return 0; decoder->impl_->Reset(bytes, offset); *result = IdentifyUnknownSectionInternal(decoder->impl_.get(), ITracer::NoTrace); return decoder->impl_->pc() - bytes.begin(); } bool ModuleDecoder::ok() { return impl_->ok(); } Result<const FunctionSig*> DecodeWasmSignatureForTesting( WasmFeatures enabled_features, Zone* zone, base::Vector<const uint8_t> bytes) { ModuleDecoderImpl decoder{enabled_features, bytes, kWasmOrigin}; return decoder.toResult(decoder.DecodeFunctionSignature(zone, bytes.begin())); } ConstantExpression DecodeWasmInitExprForTesting( WasmFeatures enabled_features, base::Vector<const uint8_t> bytes, ValueType expected) { ModuleDecoderImpl decoder{enabled_features, bytes, kWasmOrigin}; return decoder.DecodeInitExprForTesting(expected); } FunctionResult DecodeWasmFunctionForTesting( WasmFeatures enabled_features, Zone* zone, ModuleWireBytes wire_bytes, const WasmModule* module, base::Vector<const uint8_t> function_bytes) { if (function_bytes.size() > kV8MaxWasmFunctionSize) { return FunctionResult{ WasmError{0, "size > maximum function size (%zu): %zu", kV8MaxWasmFunctionSize, function_bytes.size()}}; } ModuleDecoderImpl decoder{enabled_features, function_bytes, kWasmOrigin}; return decoder.DecodeSingleFunctionForTesting(zone, wire_bytes, module); } AsmJsOffsetsResult DecodeAsmJsOffsets( base::Vector<const uint8_t> encoded_offsets) { std::vector<AsmJsOffsetFunctionEntries> functions; Decoder decoder(encoded_offsets); uint32_t functions_count = decoder.consume_u32v("functions count"); // Consistency check. DCHECK_GE(encoded_offsets.size(), functions_count); functions.reserve(functions_count); for (uint32_t i = 0; i < functions_count; ++i) { uint32_t size = decoder.consume_u32v("table size"); if (size == 0) { functions.emplace_back(); continue; } DCHECK(decoder.checkAvailable(size)); const uint8_t* table_end = decoder.pc() + size; uint32_t locals_size = decoder.consume_u32v("locals size"); int function_start_position = decoder.consume_u32v("function start pos"); int function_end_position = function_start_position; int last_byte_offset = locals_size; int last_asm_position = function_start_position; std::vector<AsmJsOffsetEntry> func_asm_offsets; func_asm_offsets.reserve(size / 4); // conservative estimation // Add an entry for the stack check, associated with position 0. func_asm_offsets.push_back( {0, function_start_position, function_start_position}); while (decoder.pc() < table_end) { DCHECK(decoder.ok()); last_byte_offset += decoder.consume_u32v("byte offset delta"); int call_position = last_asm_position + decoder.consume_i32v("call position delta"); int to_number_position = call_position + decoder.consume_i32v("to_number position delta"); last_asm_position = to_number_position; if (decoder.pc() == table_end) { // The last entry is the function end marker. DCHECK_EQ(call_position, to_number_position); function_end_position = call_position; } else { func_asm_offsets.push_back( {last_byte_offset, call_position, to_number_position}); } } DCHECK_EQ(decoder.pc(), table_end); functions.emplace_back(AsmJsOffsetFunctionEntries{ function_start_position, function_end_position, std::move(func_asm_offsets)}); } DCHECK(decoder.ok()); DCHECK(!decoder.more()); return decoder.toResult(AsmJsOffsets{std::move(functions)}); } std::vector<CustomSectionOffset> DecodeCustomSections( base::Vector<const uint8_t> bytes) { Decoder decoder(bytes); decoder.consume_bytes(4, "wasm magic"); decoder.consume_bytes(4, "wasm version"); std::vector<CustomSectionOffset> result; while (decoder.more()) { uint8_t section_code = decoder.consume_u8("section code"); uint32_t section_length = decoder.consume_u32v("section length"); uint32_t section_start = decoder.pc_offset(); if (section_code != 0) { // Skip known sections. decoder.consume_bytes(section_length, "section bytes"); continue; } uint32_t name_length = decoder.consume_u32v("name length"); uint32_t name_offset = decoder.pc_offset(); decoder.consume_bytes(name_length, "section name"); uint32_t payload_offset = decoder.pc_offset(); if (section_length < (payload_offset - section_start)) { decoder.error("invalid section length"); break; } uint32_t payload_length = section_length - (payload_offset - section_start); decoder.consume_bytes(payload_length); if (decoder.failed()) break; result.push_back({{section_start, section_length}, {name_offset, name_length}, {payload_offset, payload_length}}); } return result; } namespace { bool FindNameSection(Decoder* decoder) { static constexpr int kModuleHeaderSize = 8; decoder->consume_bytes(kModuleHeaderSize, "module header"); WasmSectionIterator section_iter(decoder, ITracer::NoTrace); while (decoder->ok() && section_iter.more() && section_iter.section_code() != kNameSectionCode) { section_iter.advance(true); } if (!section_iter.more()) return false; // Reset the decoder to not read beyond the name section end. decoder->Reset(section_iter.payload(), decoder->pc_offset()); return true; } enum class EmptyNames : bool { kAllow, kSkip }; void DecodeNameMapInternal(NameMap& target, Decoder& decoder, EmptyNames empty_names = EmptyNames::kSkip) { uint32_t count = decoder.consume_u32v("names count"); for (uint32_t i = 0; i < count; i++) { uint32_t index = decoder.consume_u32v("index"); WireBytesRef name = consume_string(&decoder, unibrow::Utf8Variant::kLossyUtf8, "name"); if (!decoder.ok()) break; if (index > NameMap::kMaxKey) continue; if (empty_names == EmptyNames::kSkip && name.is_empty()) continue; if (!validate_utf8(&decoder, name)) continue; target.Put(index, name); } target.FinishInitialization(); } void DecodeNameMap(NameMap& target, Decoder& decoder, uint32_t subsection_payload_length, EmptyNames empty_names = EmptyNames::kSkip) { if (target.is_set()) { decoder.consume_bytes(subsection_payload_length); return; } DecodeNameMapInternal(target, decoder, empty_names); } void DecodeIndirectNameMap(IndirectNameMap& target, Decoder& decoder, uint32_t subsection_payload_length) { if (target.is_set()) { decoder.consume_bytes(subsection_payload_length); return; } uint32_t outer_count = decoder.consume_u32v("outer count"); for (uint32_t i = 0; i < outer_count; ++i) { uint32_t outer_index = decoder.consume_u32v("outer index"); if (outer_index > IndirectNameMap::kMaxKey) continue; NameMap names; DecodeNameMapInternal(names, decoder); target.Put(outer_index, std::move(names)); if (!decoder.ok()) break; } target.FinishInitialization(); } } // namespace void DecodeFunctionNames(base::Vector<const uint8_t> wire_bytes, NameMap& names) { Decoder decoder(wire_bytes); if (FindNameSection(&decoder)) { while (decoder.ok() && decoder.more()) { uint8_t name_type = decoder.consume_u8("name type"); if (name_type & 0x80) break; // no varuint7 uint32_t name_payload_len = decoder.consume_u32v("name payload length"); if (!decoder.checkAvailable(name_payload_len)) break; if (name_type != NameSectionKindCode::kFunctionCode) { decoder.consume_bytes(name_payload_len, "name subsection payload"); continue; } // We need to allow empty function names for spec-conformant stack traces. DecodeNameMapInternal(names, decoder, EmptyNames::kAllow); // The spec allows only one occurrence of each subsection. We could be // more permissive and allow repeated subsections; in that case we'd // have to delay calling {target.FinishInitialization()} on the function // names map until we've seen them all. // For now, we stop decoding after finding the first function names // subsection. return; } } } namespace { // A task that validates multiple functions in parallel, storing the earliest // validation error in {this} decoder. class ValidateFunctionsTask : public JobTask { public: explicit ValidateFunctionsTask(base::Vector<const uint8_t> wire_bytes, const WasmModule* module, WasmFeatures enabled_features, std::function<bool(int)> filter, WasmError* error_out) : wire_bytes_(wire_bytes), module_(module), enabled_features_(enabled_features), filter_(std::move(filter)), next_function_(module->num_imported_functions), after_last_function_(next_function_ + module->num_declared_functions), error_out_(error_out) { DCHECK(!error_out->has_error()); } void Run(JobDelegate* delegate) override { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"), "wasm.ValidateFunctionsTask"); do { // Get the index of the next function to validate. // {fetch_add} might overrun {after_last_function_} by a bit. Since the // number of functions is limited to a value much smaller than the // integer range, this is near impossible to happen. static_assert(kV8MaxWasmFunctions < kMaxInt / 2); int func_index; do { func_index = next_function_.fetch_add(1, std::memory_order_relaxed); if (V8_UNLIKELY(func_index >= after_last_function_)) return; DCHECK_LE(0, func_index); } while ((filter_ && !filter_(func_index)) || module_->function_was_validated(func_index)); if (!ValidateFunction(func_index)) { // No need to validate any more functions. next_function_.store(after_last_function_, std::memory_order_relaxed); return; } } while (!delegate->ShouldYield()); } size_t GetMaxConcurrency(size_t /* worker_count */) const override { int next_func = next_function_.load(std::memory_order_relaxed); return std::max(0, after_last_function_ - next_func); } private: bool ValidateFunction(int func_index) { WasmFeatures unused_detected_features; const WasmFunction& function = module_->functions[func_index]; DCHECK_LT(0, function.code.offset()); FunctionBody body{function.sig, function.code.offset(), wire_bytes_.begin() + function.code.offset(), wire_bytes_.begin() + function.code.end_offset()}; DecodeResult validation_result = ValidateFunctionBody( enabled_features_, module_, &unused_detected_features, body); if (V8_UNLIKELY(validation_result.failed())) { SetError(func_index, std::move(validation_result).error()); return false; } module_->set_function_validated(func_index); return true; } // Set the error from the argument if it's earlier than the error we already // have (or if we have none yet). Thread-safe. void SetError(int func_index, WasmError error) { base::MutexGuard mutex_guard{&set_error_mutex_}; if (error_out_->has_error() && error_out_->offset() <= error.offset()) { return; } *error_out_ = GetWasmErrorWithName(wire_bytes_, func_index, module_, error); } const base::Vector<const uint8_t> wire_bytes_; const WasmModule* const module_; const WasmFeatures enabled_features_; const std::function<bool(int)> filter_; std::atomic<int> next_function_; const int after_last_function_; base::Mutex set_error_mutex_; WasmError* const error_out_; }; } // namespace WasmError ValidateFunctions(const WasmModule* module, WasmFeatures enabled_features, base::Vector<const uint8_t> wire_bytes, std::function<bool(int)> filter) { TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"), "wasm.ValidateFunctions", "num_declared_functions", module->num_declared_functions, "has_filter", filter != nullptr); DCHECK_EQ(kWasmOrigin, module->origin); class NeverYieldDelegate final : public JobDelegate { public: bool ShouldYield() override { return false; } bool IsJoiningThread() const override { UNIMPLEMENTED(); } void NotifyConcurrencyIncrease() override { UNIMPLEMENTED(); } uint8_t GetTaskId() override { UNIMPLEMENTED(); } }; // Create a {ValidateFunctionsTask} to validate all functions. The earliest // error found will be set on this decoder. WasmError validation_error; std::unique_ptr<JobTask> validate_job = std::make_unique<ValidateFunctionsTask>( wire_bytes, module, enabled_features, std::move(filter), &validation_error); if (v8_flags.single_threaded) { // In single-threaded mode, run the {ValidateFunctionsTask} synchronously. NeverYieldDelegate delegate; validate_job->Run(&delegate); } else { // Spawn the task and join it. std::unique_ptr<JobHandle> job_handle = V8::GetCurrentPlatform()->CreateJob( TaskPriority::kUserVisible, std::move(validate_job)); job_handle->Join(); } return validation_error; } WasmError GetWasmErrorWithName(base::Vector<const uint8_t> wire_bytes, int func_index, const WasmModule* module, WasmError error) { WasmName name = ModuleWireBytes{wire_bytes}.GetNameOrNull(func_index, module); if (name.begin() == nullptr) { return WasmError(error.offset(), "Compiling function #%d failed: %s", func_index, error.message().c_str()); } else { TruncatedUserString<> truncated_name(name); return WasmError(error.offset(), "Compiling function #%d:\"%.*s\" failed: %s", func_index, truncated_name.length(), truncated_name.start(), error.message().c_str()); } } DecodedNameSection::DecodedNameSection(base::Vector<const uint8_t> wire_bytes, WireBytesRef name_section) { if (name_section.is_empty()) return; // No name section. Decoder decoder(wire_bytes.begin() + name_section.offset(), wire_bytes.begin() + name_section.end_offset(), name_section.offset()); while (decoder.ok() && decoder.more()) { uint8_t name_type = decoder.consume_u8("name type"); if (name_type & 0x80) break; // no varuint7 uint32_t name_payload_len = decoder.consume_u32v("name payload length"); if (!decoder.checkAvailable(name_payload_len)) break; switch (name_type) { case kModuleCode: case kFunctionCode: // Already handled elsewhere. decoder.consume_bytes(name_payload_len); break; case kLocalCode: static_assert(kV8MaxWasmFunctions <= IndirectNameMap::kMaxKey); static_assert(kV8MaxWasmFunctionLocals <= NameMap::kMaxKey); DecodeIndirectNameMap(local_names_, decoder, name_payload_len); break; case kLabelCode: static_assert(kV8MaxWasmFunctions <= IndirectNameMap::kMaxKey); static_assert(kV8MaxWasmFunctionSize <= NameMap::kMaxKey); DecodeIndirectNameMap(label_names_, decoder, name_payload_len); break; case kTypeCode: static_assert(kV8MaxWasmTypes <= NameMap::kMaxKey); DecodeNameMap(type_names_, decoder, name_payload_len); break; case kTableCode: static_assert(kV8MaxWasmTables <= NameMap::kMaxKey); DecodeNameMap(table_names_, decoder, name_payload_len); break; case kMemoryCode: static_assert(kV8MaxWasmMemories <= NameMap::kMaxKey); DecodeNameMap(memory_names_, decoder, name_payload_len); break; case kGlobalCode: static_assert(kV8MaxWasmGlobals <= NameMap::kMaxKey); DecodeNameMap(global_names_, decoder, name_payload_len); break; case kElementSegmentCode: static_assert(kV8MaxWasmTableInitEntries <= NameMap::kMaxKey); DecodeNameMap(element_segment_names_, decoder, name_payload_len); break; case kDataSegmentCode: static_assert(kV8MaxWasmDataSegments <= NameMap::kMaxKey); DecodeNameMap(data_segment_names_, decoder, name_payload_len); break; case kFieldCode: static_assert(kV8MaxWasmTypes <= IndirectNameMap::kMaxKey); static_assert(kV8MaxWasmStructFields <= NameMap::kMaxKey); DecodeIndirectNameMap(field_names_, decoder, name_payload_len); break; case kTagCode: static_assert(kV8MaxWasmTags <= NameMap::kMaxKey); DecodeNameMap(tag_names_, decoder, name_payload_len); break; } } } #undef TRACE } // namespace wasm } // namespace internal } // namespace v8