| File: | out/../deps/v8/src/wasm/wasm-module.h |
| Warning: | line 443, column 35 Forming reference to null pointer |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // Copyright 2015 the V8 project authors. All rights reserved. | ||||
| 2 | // Use of this source code is governed by a BSD-style license that can be | ||||
| 3 | // found in the LICENSE file. | ||||
| 4 | |||||
| 5 | #include "src/wasm/module-decoder.h" | ||||
| 6 | |||||
| 7 | #include "src/base/functional.h" | ||||
| 8 | #include "src/base/platform/platform.h" | ||||
| 9 | #include "src/base/platform/wrappers.h" | ||||
| 10 | #include "src/flags/flags.h" | ||||
| 11 | #include "src/init/v8.h" | ||||
| 12 | #include "src/logging/counters.h" | ||||
| 13 | #include "src/logging/metrics.h" | ||||
| 14 | #include "src/objects/objects-inl.h" | ||||
| 15 | #include "src/utils/ostreams.h" | ||||
| 16 | #include "src/wasm/canonical-types.h" | ||||
| 17 | #include "src/wasm/decoder.h" | ||||
| 18 | #include "src/wasm/function-body-decoder-impl.h" | ||||
| 19 | #include "src/wasm/init-expr-interface.h" | ||||
| 20 | #include "src/wasm/struct-types.h" | ||||
| 21 | #include "src/wasm/wasm-constants.h" | ||||
| 22 | #include "src/wasm/wasm-engine.h" | ||||
| 23 | #include "src/wasm/wasm-limits.h" | ||||
| 24 | #include "src/wasm/wasm-opcodes-inl.h" | ||||
| 25 | |||||
| 26 | namespace v8 { | ||||
| 27 | namespace internal { | ||||
| 28 | namespace wasm { | ||||
| 29 | |||||
| 30 | #define TRACE(...) \ | ||||
| 31 | do { \ | ||||
| 32 | if (FLAG_trace_wasm_decoder) PrintF(__VA_ARGS__); \ | ||||
| 33 | } while (false) | ||||
| 34 | |||||
| 35 | namespace { | ||||
| 36 | |||||
| 37 | constexpr char kNameString[] = "name"; | ||||
| 38 | constexpr char kSourceMappingURLString[] = "sourceMappingURL"; | ||||
| 39 | constexpr char kCompilationHintsString[] = "compilationHints"; | ||||
| 40 | constexpr char kBranchHintsString[] = "metadata.code.branch_hint"; | ||||
| 41 | constexpr char kDebugInfoString[] = ".debug_info"; | ||||
| 42 | constexpr char kExternalDebugInfoString[] = "external_debug_info"; | ||||
| 43 | |||||
| 44 | const char* ExternalKindName(ImportExportKindCode kind) { | ||||
| 45 | switch (kind) { | ||||
| 46 | case kExternalFunction: | ||||
| 47 | return "function"; | ||||
| 48 | case kExternalTable: | ||||
| 49 | return "table"; | ||||
| 50 | case kExternalMemory: | ||||
| 51 | return "memory"; | ||||
| 52 | case kExternalGlobal: | ||||
| 53 | return "global"; | ||||
| 54 | case kExternalTag: | ||||
| 55 | return "tag"; | ||||
| 56 | } | ||||
| 57 | return "unknown"; | ||||
| 58 | } | ||||
| 59 | |||||
| 60 | } // namespace | ||||
| 61 | |||||
| 62 | const char* SectionName(SectionCode code) { | ||||
| 63 | switch (code) { | ||||
| 64 | case kUnknownSectionCode: | ||||
| 65 | return "Unknown"; | ||||
| 66 | case kTypeSectionCode: | ||||
| 67 | return "Type"; | ||||
| 68 | case kImportSectionCode: | ||||
| 69 | return "Import"; | ||||
| 70 | case kFunctionSectionCode: | ||||
| 71 | return "Function"; | ||||
| 72 | case kTableSectionCode: | ||||
| 73 | return "Table"; | ||||
| 74 | case kMemorySectionCode: | ||||
| 75 | return "Memory"; | ||||
| 76 | case kGlobalSectionCode: | ||||
| 77 | return "Global"; | ||||
| 78 | case kExportSectionCode: | ||||
| 79 | return "Export"; | ||||
| 80 | case kStartSectionCode: | ||||
| 81 | return "Start"; | ||||
| 82 | case kCodeSectionCode: | ||||
| 83 | return "Code"; | ||||
| 84 | case kElementSectionCode: | ||||
| 85 | return "Element"; | ||||
| 86 | case kDataSectionCode: | ||||
| 87 | return "Data"; | ||||
| 88 | case kTagSectionCode: | ||||
| 89 | return "Tag"; | ||||
| 90 | case kDataCountSectionCode: | ||||
| 91 | return "DataCount"; | ||||
| 92 | case kNameSectionCode: | ||||
| 93 | return kNameString; | ||||
| 94 | case kSourceMappingURLSectionCode: | ||||
| 95 | return kSourceMappingURLString; | ||||
| 96 | case kDebugInfoSectionCode: | ||||
| 97 | return kDebugInfoString; | ||||
| 98 | case kExternalDebugInfoSectionCode: | ||||
| 99 | return kExternalDebugInfoString; | ||||
| 100 | case kCompilationHintsSectionCode: | ||||
| 101 | return kCompilationHintsString; | ||||
| 102 | case kBranchHintsSectionCode: | ||||
| 103 | return kBranchHintsString; | ||||
| 104 | default: | ||||
| 105 | return "<unknown>"; | ||||
| 106 | } | ||||
| 107 | } | ||||
| 108 | |||||
| 109 | namespace { | ||||
| 110 | |||||
| 111 | bool validate_utf8(Decoder* decoder, WireBytesRef string) { | ||||
| 112 | return unibrow::Utf8::ValidateEncoding( | ||||
| 113 | decoder->start() + decoder->GetBufferRelativeOffset(string.offset()), | ||||
| 114 | string.length()); | ||||
| 115 | } | ||||
| 116 | |||||
| 117 | // Reads a length-prefixed string, checking that it is within bounds. Returns | ||||
| 118 | // the offset of the string, and the length as an out parameter. | ||||
| 119 | WireBytesRef consume_string(Decoder* decoder, bool validate_utf8, | ||||
| 120 | const char* name) { | ||||
| 121 | uint32_t length = decoder->consume_u32v("string length"); | ||||
| 122 | uint32_t offset = decoder->pc_offset(); | ||||
| 123 | const byte* string_start = decoder->pc(); | ||||
| 124 | // Consume bytes before validation to guarantee that the string is not oob. | ||||
| 125 | if (length > 0) { | ||||
| 126 | decoder->consume_bytes(length, name); | ||||
| 127 | if (decoder->ok() && validate_utf8 && | ||||
| 128 | !unibrow::Utf8::ValidateEncoding(string_start, length)) { | ||||
| 129 | decoder->errorf(string_start, "%s: no valid UTF-8 string", name); | ||||
| 130 | } | ||||
| 131 | } | ||||
| 132 | return {offset, decoder->failed() ? 0 : length}; | ||||
| 133 | } | ||||
| 134 | |||||
| 135 | namespace { | ||||
| 136 | SectionCode IdentifyUnknownSectionInternal(Decoder* decoder) { | ||||
| 137 | WireBytesRef string = consume_string(decoder, true, "section name"); | ||||
| 138 | if (decoder->failed()) { | ||||
| 139 | return kUnknownSectionCode; | ||||
| 140 | } | ||||
| 141 | const byte* section_name_start = | ||||
| 142 | decoder->start() + decoder->GetBufferRelativeOffset(string.offset()); | ||||
| 143 | |||||
| 144 | TRACE(" +%d section name : \"%.*s\"\n", | ||||
| 145 | static_cast<int>(section_name_start - decoder->start()), | ||||
| 146 | string.length() < 20 ? string.length() : 20, section_name_start); | ||||
| 147 | |||||
| 148 | using SpecialSectionPair = std::pair<base::Vector<const char>, SectionCode>; | ||||
| 149 | static constexpr SpecialSectionPair kSpecialSections[]{ | ||||
| 150 | {base::StaticCharVector(kNameString), kNameSectionCode}, | ||||
| 151 | {base::StaticCharVector(kSourceMappingURLString), | ||||
| 152 | kSourceMappingURLSectionCode}, | ||||
| 153 | {base::StaticCharVector(kCompilationHintsString), | ||||
| 154 | kCompilationHintsSectionCode}, | ||||
| 155 | {base::StaticCharVector(kBranchHintsString), kBranchHintsSectionCode}, | ||||
| 156 | {base::StaticCharVector(kDebugInfoString), kDebugInfoSectionCode}, | ||||
| 157 | {base::StaticCharVector(kExternalDebugInfoString), | ||||
| 158 | kExternalDebugInfoSectionCode}}; | ||||
| 159 | |||||
| 160 | auto name_vec = base::Vector<const char>::cast( | ||||
| 161 | base::VectorOf(section_name_start, string.length())); | ||||
| 162 | for (auto& special_section : kSpecialSections) { | ||||
| 163 | if (name_vec == special_section.first) return special_section.second; | ||||
| 164 | } | ||||
| 165 | |||||
| 166 | return kUnknownSectionCode; | ||||
| 167 | } | ||||
| 168 | } // namespace | ||||
| 169 | |||||
| 170 | // An iterator over the sections in a wasm binary module. | ||||
| 171 | // Automatically skips all unknown sections. | ||||
| 172 | class WasmSectionIterator { | ||||
| 173 | public: | ||||
| 174 | explicit WasmSectionIterator(Decoder* decoder) | ||||
| 175 | : decoder_(decoder), | ||||
| 176 | section_code_(kUnknownSectionCode), | ||||
| 177 | section_start_(decoder->pc()), | ||||
| 178 | section_end_(decoder->pc()) { | ||||
| 179 | next(); | ||||
| 180 | } | ||||
| 181 | |||||
| 182 | bool more() const { return decoder_->ok() && decoder_->more(); } | ||||
| 183 | |||||
| 184 | SectionCode section_code() const { return section_code_; } | ||||
| 185 | |||||
| 186 | const byte* section_start() const { return section_start_; } | ||||
| 187 | |||||
| 188 | uint32_t section_length() const { | ||||
| 189 | return static_cast<uint32_t>(section_end_ - section_start_); | ||||
| 190 | } | ||||
| 191 | |||||
| 192 | base::Vector<const uint8_t> payload() const { | ||||
| 193 | return {payload_start_, payload_length()}; | ||||
| 194 | } | ||||
| 195 | |||||
| 196 | const byte* payload_start() const { return payload_start_; } | ||||
| 197 | |||||
| 198 | uint32_t payload_length() const { | ||||
| 199 | return static_cast<uint32_t>(section_end_ - payload_start_); | ||||
| 200 | } | ||||
| 201 | |||||
| 202 | const byte* section_end() const { return section_end_; } | ||||
| 203 | |||||
| 204 | // Advances to the next section, checking that decoding the current section | ||||
| 205 | // stopped at {section_end_}. | ||||
| 206 | void advance(bool move_to_section_end = false) { | ||||
| 207 | if (move_to_section_end && decoder_->pc() < section_end_) { | ||||
| 208 | decoder_->consume_bytes( | ||||
| 209 | static_cast<uint32_t>(section_end_ - decoder_->pc())); | ||||
| 210 | } | ||||
| 211 | if (decoder_->pc() != section_end_) { | ||||
| 212 | const char* msg = decoder_->pc() < section_end_ ? "shorter" : "longer"; | ||||
| 213 | decoder_->errorf(decoder_->pc(), | ||||
| 214 | "section was %s than expected size " | ||||
| 215 | "(%u bytes expected, %zu decoded)", | ||||
| 216 | msg, section_length(), | ||||
| 217 | static_cast<size_t>(decoder_->pc() - section_start_)); | ||||
| 218 | } | ||||
| 219 | next(); | ||||
| 220 | } | ||||
| 221 | |||||
| 222 | private: | ||||
| 223 | Decoder* decoder_; | ||||
| 224 | SectionCode section_code_; | ||||
| 225 | const byte* section_start_; | ||||
| 226 | const byte* payload_start_; | ||||
| 227 | const byte* section_end_; | ||||
| 228 | |||||
| 229 | // Reads the section code/name at the current position and sets up | ||||
| 230 | // the embedder fields. | ||||
| 231 | void next() { | ||||
| 232 | if (!decoder_->more()) { | ||||
| 233 | section_code_ = kUnknownSectionCode; | ||||
| 234 | return; | ||||
| 235 | } | ||||
| 236 | section_start_ = decoder_->pc(); | ||||
| 237 | uint8_t section_code = decoder_->consume_u8("section code"); | ||||
| 238 | // Read and check the section size. | ||||
| 239 | uint32_t section_length = decoder_->consume_u32v("section length"); | ||||
| 240 | |||||
| 241 | payload_start_ = decoder_->pc(); | ||||
| 242 | if (decoder_->checkAvailable(section_length)) { | ||||
| 243 | // Get the limit of the section within the module. | ||||
| 244 | section_end_ = payload_start_ + section_length; | ||||
| 245 | } else { | ||||
| 246 | // The section would extend beyond the end of the module. | ||||
| 247 | section_end_ = payload_start_; | ||||
| 248 | } | ||||
| 249 | |||||
| 250 | if (section_code == kUnknownSectionCode) { | ||||
| 251 | // Check for the known "name", "sourceMappingURL", or "compilationHints" | ||||
| 252 | // section. | ||||
| 253 | // To identify the unknown section we set the end of the decoder bytes to | ||||
| 254 | // the end of the custom section, so that we do not read the section name | ||||
| 255 | // beyond the end of the section. | ||||
| 256 | const byte* module_end = decoder_->end(); | ||||
| 257 | decoder_->set_end(section_end_); | ||||
| 258 | section_code = IdentifyUnknownSectionInternal(decoder_); | ||||
| 259 | if (decoder_->ok()) decoder_->set_end(module_end); | ||||
| 260 | // As a side effect, the above function will forward the decoder to after | ||||
| 261 | // the identifier string. | ||||
| 262 | payload_start_ = decoder_->pc(); | ||||
| 263 | } else if (!IsValidSectionCode(section_code)) { | ||||
| 264 | decoder_->errorf(decoder_->pc(), "unknown section code #0x%02x", | ||||
| 265 | section_code); | ||||
| 266 | section_code = kUnknownSectionCode; | ||||
| 267 | } | ||||
| 268 | section_code_ = decoder_->failed() ? kUnknownSectionCode | ||||
| 269 | : static_cast<SectionCode>(section_code); | ||||
| 270 | |||||
| 271 | if (section_code_ == kUnknownSectionCode && section_end_ > decoder_->pc()) { | ||||
| 272 | // skip to the end of the unknown section. | ||||
| 273 | uint32_t remaining = static_cast<uint32_t>(section_end_ - decoder_->pc()); | ||||
| 274 | decoder_->consume_bytes(remaining, "section payload"); | ||||
| 275 | } | ||||
| 276 | } | ||||
| 277 | }; | ||||
| 278 | |||||
| 279 | } // namespace | ||||
| 280 | |||||
| 281 | // The main logic for decoding the bytes of a module. | ||||
| 282 | class ModuleDecoderImpl : public Decoder { | ||||
| 283 | public: | ||||
| 284 | explicit ModuleDecoderImpl(const WasmFeatures& enabled, ModuleOrigin origin) | ||||
| 285 | : Decoder(nullptr, nullptr), | ||||
| 286 | enabled_features_(enabled), | ||||
| 287 | origin_(origin) {} | ||||
| 288 | |||||
| 289 | ModuleDecoderImpl(const WasmFeatures& enabled, const byte* module_start, | ||||
| 290 | const byte* module_end, ModuleOrigin origin) | ||||
| 291 | : Decoder(module_start, module_end), | ||||
| 292 | enabled_features_(enabled), | ||||
| 293 | module_start_(module_start), | ||||
| 294 | module_end_(module_end), | ||||
| 295 | origin_(origin) { | ||||
| 296 | if (end_ < start_) { | ||||
| 297 | error(start_, "end is less than start"); | ||||
| 298 | end_ = start_; | ||||
| 299 | } | ||||
| 300 | } | ||||
| 301 | |||||
| 302 | void onFirstError() override { | ||||
| 303 | pc_ = end_; // On error, terminate section decoding loop. | ||||
| 304 | } | ||||
| 305 | |||||
| 306 | void DumpModule(const base::Vector<const byte> module_bytes) { | ||||
| 307 | std::string path; | ||||
| 308 | if (FLAG_dump_wasm_module_path) { | ||||
| 309 | path = FLAG_dump_wasm_module_path; | ||||
| 310 | if (path.size() && | ||||
| 311 | !base::OS::isDirectorySeparator(path[path.size() - 1])) { | ||||
| 312 | path += base::OS::DirectorySeparator(); | ||||
| 313 | } | ||||
| 314 | } | ||||
| 315 | // File are named `HASH.{ok,failed}.wasm`. | ||||
| 316 | size_t hash = base::hash_range(module_bytes.begin(), module_bytes.end()); | ||||
| 317 | base::EmbeddedVector<char, 32> buf; | ||||
| 318 | SNPrintF(buf, "%016zx.%s.wasm", hash, ok() ? "ok" : "failed"); | ||||
| 319 | path += buf.begin(); | ||||
| 320 | size_t rv = 0; | ||||
| 321 | if (FILE* file = base::OS::FOpen(path.c_str(), "wb")) { | ||||
| 322 | rv = fwrite(module_bytes.begin(), module_bytes.length(), 1, file); | ||||
| 323 | base::Fclose(file); | ||||
| 324 | } | ||||
| 325 | if (rv != 1) { | ||||
| 326 | OFStream os(stderrstderr); | ||||
| 327 | os << "Error while dumping wasm file to " << path << std::endl; | ||||
| 328 | } | ||||
| 329 | } | ||||
| 330 | |||||
| 331 | void StartDecoding(Counters* counters, AccountingAllocator* allocator) { | ||||
| 332 | CHECK_NULL(module_)do { if ((__builtin_expect(!!(!((module_) == nullptr)), 0))) { V8_Fatal("Check failed: %s.", "(module_) == nullptr"); } } while (false); | ||||
| 333 | SetCounters(counters); | ||||
| 334 | module_.reset( | ||||
| 335 | new WasmModule(std::make_unique<Zone>(allocator, "signatures"))); | ||||
| 336 | module_->initial_pages = 0; | ||||
| 337 | module_->maximum_pages = 0; | ||||
| 338 | module_->mem_export = false; | ||||
| 339 | module_->origin = origin_; | ||||
| 340 | } | ||||
| 341 | |||||
| 342 | void DecodeModuleHeader(base::Vector<const uint8_t> bytes, uint8_t offset) { | ||||
| 343 | if (failed()) return; | ||||
| 344 | Reset(bytes, offset); | ||||
| 345 | |||||
| 346 | const byte* pos = pc_; | ||||
| 347 | uint32_t magic_word = consume_u32("wasm magic"); | ||||
| 348 | #define BYTES(x) (x & 0xFF), (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF | ||||
| 349 | if (magic_word != kWasmMagic) { | ||||
| 350 | errorf(pos, | ||||
| 351 | "expected magic word %02x %02x %02x %02x, " | ||||
| 352 | "found %02x %02x %02x %02x", | ||||
| 353 | BYTES(kWasmMagic), BYTES(magic_word)); | ||||
| 354 | } | ||||
| 355 | |||||
| 356 | pos = pc_; | ||||
| 357 | { | ||||
| 358 | uint32_t magic_version = consume_u32("wasm version"); | ||||
| 359 | if (magic_version != kWasmVersion) { | ||||
| 360 | errorf(pos, | ||||
| 361 | "expected version %02x %02x %02x %02x, " | ||||
| 362 | "found %02x %02x %02x %02x", | ||||
| 363 | BYTES(kWasmVersion), BYTES(magic_version)); | ||||
| 364 | } | ||||
| 365 | } | ||||
| 366 | #undef BYTES | ||||
| 367 | } | ||||
| 368 | |||||
| 369 | bool CheckSectionOrder(SectionCode section_code, | ||||
| 370 | SectionCode prev_section_code, | ||||
| 371 | SectionCode next_section_code) { | ||||
| 372 | if (next_ordered_section_ > next_section_code) { | ||||
| 373 | errorf(pc(), "The %s section must appear before the %s section", | ||||
| 374 | SectionName(section_code), SectionName(next_section_code)); | ||||
| 375 | return false; | ||||
| 376 | } | ||||
| 377 | if (next_ordered_section_ <= prev_section_code) { | ||||
| 378 | next_ordered_section_ = prev_section_code + 1; | ||||
| 379 | } | ||||
| 380 | return true; | ||||
| 381 | } | ||||
| 382 | |||||
| 383 | bool CheckUnorderedSection(SectionCode section_code) { | ||||
| 384 | if (has_seen_unordered_section(section_code)) { | ||||
| 385 | errorf(pc(), "Multiple %s sections not allowed", | ||||
| 386 | SectionName(section_code)); | ||||
| 387 | return false; | ||||
| 388 | } | ||||
| 389 | set_seen_unordered_section(section_code); | ||||
| 390 | return true; | ||||
| 391 | } | ||||
| 392 | |||||
| 393 | void DecodeSection(SectionCode section_code, | ||||
| 394 | base::Vector<const uint8_t> bytes, uint32_t offset, | ||||
| 395 | bool verify_functions = true) { | ||||
| 396 | if (failed()) return; | ||||
| 397 | Reset(bytes, offset); | ||||
| 398 | TRACE("Section: %s\n", SectionName(section_code)); | ||||
| 399 | TRACE("Decode Section %p - %p\n", bytes.begin(), bytes.end()); | ||||
| 400 | |||||
| 401 | // Check if the section is out-of-order. | ||||
| 402 | if (section_code < next_ordered_section_ && | ||||
| 403 | section_code < kFirstUnorderedSection) { | ||||
| 404 | errorf(pc(), "unexpected section <%s>", SectionName(section_code)); | ||||
| 405 | return; | ||||
| 406 | } | ||||
| 407 | |||||
| 408 | switch (section_code) { | ||||
| 409 | case kUnknownSectionCode: | ||||
| 410 | break; | ||||
| 411 | case kDataCountSectionCode: | ||||
| 412 | if (!CheckUnorderedSection(section_code)) return; | ||||
| 413 | // If wasm-gc is enabled, we allow the data cound section anywhere in | ||||
| 414 | // the module. | ||||
| 415 | if (!enabled_features_.has_gc() && | ||||
| 416 | !CheckSectionOrder(section_code, kElementSectionCode, | ||||
| 417 | kCodeSectionCode)) { | ||||
| 418 | return; | ||||
| 419 | } | ||||
| 420 | break; | ||||
| 421 | case kTagSectionCode: | ||||
| 422 | if (!CheckUnorderedSection(section_code)) return; | ||||
| 423 | if (!CheckSectionOrder(section_code, kMemorySectionCode, | ||||
| 424 | kGlobalSectionCode)) { | ||||
| 425 | return; | ||||
| 426 | } | ||||
| 427 | break; | ||||
| 428 | case kNameSectionCode: | ||||
| 429 | // TODO(titzer): report out of place name section as a warning. | ||||
| 430 | // Be lenient with placement of name section. All except first | ||||
| 431 | // occurrence are ignored. | ||||
| 432 | case kSourceMappingURLSectionCode: | ||||
| 433 | // sourceMappingURL is a custom section and currently can occur anywhere | ||||
| 434 | // in the module. In case of multiple sourceMappingURL sections, all | ||||
| 435 | // except the first occurrence are ignored. | ||||
| 436 | case kDebugInfoSectionCode: | ||||
| 437 | // .debug_info is a custom section containing core DWARF information | ||||
| 438 | // if produced by compiler. Its presence likely means that Wasm was | ||||
| 439 | // built in a debug mode. | ||||
| 440 | case kExternalDebugInfoSectionCode: | ||||
| 441 | // external_debug_info is a custom section containing a reference to an | ||||
| 442 | // external symbol file. | ||||
| 443 | case kCompilationHintsSectionCode: | ||||
| 444 | // TODO(frgossen): report out of place compilation hints section as a | ||||
| 445 | // warning. | ||||
| 446 | // Be lenient with placement of compilation hints section. All except | ||||
| 447 | // first occurrence after function section and before code section are | ||||
| 448 | // ignored. | ||||
| 449 | break; | ||||
| 450 | case kBranchHintsSectionCode: | ||||
| 451 | // TODO(yuri): report out of place branch hints section as a | ||||
| 452 | // warning. | ||||
| 453 | // Be lenient with placement of compilation hints section. All except | ||||
| 454 | // first occurrence after function section and before code section are | ||||
| 455 | // ignored. | ||||
| 456 | break; | ||||
| 457 | default: | ||||
| 458 | next_ordered_section_ = section_code + 1; | ||||
| 459 | break; | ||||
| 460 | } | ||||
| 461 | |||||
| 462 | switch (section_code) { | ||||
| 463 | case kUnknownSectionCode: | ||||
| 464 | break; | ||||
| 465 | case kTypeSectionCode: | ||||
| 466 | DecodeTypeSection(); | ||||
| 467 | break; | ||||
| 468 | case kImportSectionCode: | ||||
| 469 | DecodeImportSection(); | ||||
| 470 | break; | ||||
| 471 | case kFunctionSectionCode: | ||||
| 472 | DecodeFunctionSection(); | ||||
| 473 | break; | ||||
| 474 | case kTableSectionCode: | ||||
| 475 | DecodeTableSection(); | ||||
| 476 | break; | ||||
| 477 | case kMemorySectionCode: | ||||
| 478 | DecodeMemorySection(); | ||||
| 479 | break; | ||||
| 480 | case kGlobalSectionCode: | ||||
| 481 | DecodeGlobalSection(); | ||||
| 482 | break; | ||||
| 483 | case kExportSectionCode: | ||||
| 484 | DecodeExportSection(); | ||||
| 485 | break; | ||||
| 486 | case kStartSectionCode: | ||||
| 487 | DecodeStartSection(); | ||||
| 488 | break; | ||||
| 489 | case kCodeSectionCode: | ||||
| 490 | DecodeCodeSection(verify_functions); | ||||
| 491 | break; | ||||
| 492 | case kElementSectionCode: | ||||
| 493 | DecodeElementSection(); | ||||
| 494 | break; | ||||
| 495 | case kDataSectionCode: | ||||
| 496 | DecodeDataSection(); | ||||
| 497 | break; | ||||
| 498 | case kNameSectionCode: | ||||
| 499 | DecodeNameSection(); | ||||
| 500 | break; | ||||
| 501 | case kSourceMappingURLSectionCode: | ||||
| 502 | DecodeSourceMappingURLSection(); | ||||
| 503 | break; | ||||
| 504 | case kDebugInfoSectionCode: | ||||
| 505 | // If there is an explicit source map, prefer it over DWARF info. | ||||
| 506 | if (module_->debug_symbols.type == WasmDebugSymbols::Type::None) { | ||||
| 507 | module_->debug_symbols = {WasmDebugSymbols::Type::EmbeddedDWARF, {}}; | ||||
| 508 | } | ||||
| 509 | consume_bytes(static_cast<uint32_t>(end_ - start_), ".debug_info"); | ||||
| 510 | break; | ||||
| 511 | case kExternalDebugInfoSectionCode: | ||||
| 512 | DecodeExternalDebugInfoSection(); | ||||
| 513 | break; | ||||
| 514 | case kCompilationHintsSectionCode: | ||||
| 515 | if (enabled_features_.has_compilation_hints()) { | ||||
| 516 | DecodeCompilationHintsSection(); | ||||
| 517 | } else { | ||||
| 518 | // Ignore this section when feature was disabled. It is an optional | ||||
| 519 | // custom section anyways. | ||||
| 520 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
| 521 | } | ||||
| 522 | break; | ||||
| 523 | case kBranchHintsSectionCode: | ||||
| 524 | if (enabled_features_.has_branch_hinting()) { | ||||
| 525 | DecodeBranchHintsSection(); | ||||
| 526 | } else { | ||||
| 527 | // Ignore this section when feature was disabled. It is an optional | ||||
| 528 | // custom section anyways. | ||||
| 529 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
| 530 | } | ||||
| 531 | break; | ||||
| 532 | case kDataCountSectionCode: | ||||
| 533 | DecodeDataCountSection(); | ||||
| 534 | break; | ||||
| 535 | case kTagSectionCode: | ||||
| 536 | if (enabled_features_.has_eh()) { | ||||
| 537 | DecodeTagSection(); | ||||
| 538 | } else { | ||||
| 539 | errorf(pc(), | ||||
| 540 | "unexpected section <%s> (enable with --experimental-wasm-eh)", | ||||
| 541 | SectionName(section_code)); | ||||
| 542 | } | ||||
| 543 | break; | ||||
| 544 | default: | ||||
| 545 | errorf(pc(), "unexpected section <%s>", SectionName(section_code)); | ||||
| 546 | return; | ||||
| 547 | } | ||||
| 548 | |||||
| 549 | if (pc() != bytes.end()) { | ||||
| 550 | const char* msg = pc() < bytes.end() ? "shorter" : "longer"; | ||||
| 551 | errorf(pc(), | ||||
| 552 | "section was %s than expected size " | ||||
| 553 | "(%zu bytes expected, %zu decoded)", | ||||
| 554 | msg, bytes.size(), static_cast<size_t>(pc() - bytes.begin())); | ||||
| 555 | } | ||||
| 556 | } | ||||
| 557 | |||||
| 558 | TypeDefinition consume_base_type_definition() { | ||||
| 559 | DCHECK(enabled_features_.has_gc())((void) 0); | ||||
| 560 | uint8_t kind = consume_u8("type kind"); | ||||
| 561 | switch (kind) { | ||||
| 562 | case kWasmFunctionTypeCode: { | ||||
| 563 | const FunctionSig* sig = consume_sig(module_->signature_zone.get()); | ||||
| 564 | return {sig, kNoSuperType}; | ||||
| 565 | } | ||||
| 566 | case kWasmStructTypeCode: { | ||||
| 567 | const StructType* type = consume_struct(module_->signature_zone.get()); | ||||
| 568 | return {type, kNoSuperType}; | ||||
| 569 | } | ||||
| 570 | case kWasmArrayTypeCode: { | ||||
| 571 | const ArrayType* type = consume_array(module_->signature_zone.get()); | ||||
| 572 | return {type, kNoSuperType}; | ||||
| 573 | } | ||||
| 574 | case kWasmFunctionNominalCode: | ||||
| 575 | case kWasmArrayNominalCode: | ||||
| 576 | case kWasmStructNominalCode: | ||||
| 577 | errorf(pc() - 1, | ||||
| 578 | "mixing nominal and isorecursive types is not allowed"); | ||||
| 579 | return {}; | ||||
| 580 | default: | ||||
| 581 | errorf(pc() - 1, "unknown type form: %d", kind); | ||||
| 582 | return {}; | ||||
| 583 | } | ||||
| 584 | } | ||||
| 585 | |||||
| 586 | bool check_supertype(uint32_t supertype) { | ||||
| 587 | if (V8_UNLIKELY(supertype >= module_->types.size())(__builtin_expect(!!(supertype >= module_->types.size() ), 0))) { | ||||
| 588 | errorf(pc(), "type %zu: forward-declared supertype %d", | ||||
| 589 | module_->types.size(), supertype); | ||||
| 590 | return false; | ||||
| 591 | } | ||||
| 592 | return true; | ||||
| 593 | } | ||||
| 594 | |||||
| 595 | TypeDefinition consume_nominal_type_definition() { | ||||
| 596 | DCHECK(enabled_features_.has_gc())((void) 0); | ||||
| 597 | size_t num_types = module_->types.size(); | ||||
| 598 | uint8_t kind = consume_u8("type kind"); | ||||
| 599 | switch (kind) { | ||||
| 600 | case kWasmFunctionNominalCode: { | ||||
| 601 | const FunctionSig* sig = consume_sig(module_->signature_zone.get()); | ||||
| 602 | uint32_t super_index = kNoSuperType; | ||||
| 603 | HeapType super_type = consume_super_type(); | ||||
| 604 | if (super_type.is_index()) { | ||||
| 605 | super_index = super_type.representation(); | ||||
| 606 | } else if (V8_UNLIKELY(super_type != HeapType::kFunc)(__builtin_expect(!!(super_type != HeapType::kFunc), 0))) { | ||||
| 607 | errorf(pc() - 1, "type %zu: invalid supertype %d", num_types, | ||||
| 608 | super_type.code()); | ||||
| 609 | return {}; | ||||
| 610 | } | ||||
| 611 | return {sig, super_index}; | ||||
| 612 | } | ||||
| 613 | case kWasmStructNominalCode: { | ||||
| 614 | const StructType* type = consume_struct(module_->signature_zone.get()); | ||||
| 615 | uint32_t super_index = kNoSuperType; | ||||
| 616 | HeapType super_type = consume_super_type(); | ||||
| 617 | if (super_type.is_index()) { | ||||
| 618 | super_index = super_type.representation(); | ||||
| 619 | } else if (V8_UNLIKELY(super_type != HeapType::kData)(__builtin_expect(!!(super_type != HeapType::kData), 0))) { | ||||
| 620 | errorf(pc() - 1, "type %zu: invalid supertype %d", num_types, | ||||
| 621 | super_type.code()); | ||||
| 622 | return {}; | ||||
| 623 | } | ||||
| 624 | return {type, super_index}; | ||||
| 625 | } | ||||
| 626 | case kWasmArrayNominalCode: { | ||||
| 627 | const ArrayType* type = consume_array(module_->signature_zone.get()); | ||||
| 628 | uint32_t super_index = kNoSuperType; | ||||
| 629 | HeapType super_type = consume_super_type(); | ||||
| 630 | if (super_type.is_index()) { | ||||
| 631 | super_index = super_type.representation(); | ||||
| 632 | } else if (V8_UNLIKELY(super_type != HeapType::kData)(__builtin_expect(!!(super_type != HeapType::kData), 0))) { | ||||
| 633 | errorf(pc() - 1, "type %zu: invalid supertype %d", num_types, | ||||
| 634 | super_type.code()); | ||||
| 635 | return {}; | ||||
| 636 | } | ||||
| 637 | return {type, super_index}; | ||||
| 638 | } | ||||
| 639 | case kWasmFunctionTypeCode: | ||||
| 640 | case kWasmArrayTypeCode: | ||||
| 641 | case kWasmStructTypeCode: | ||||
| 642 | case kWasmSubtypeCode: | ||||
| 643 | case kWasmRecursiveTypeGroupCode: | ||||
| 644 | errorf(pc() - 1, | ||||
| 645 | "mixing nominal and isorecursive types is not allowed"); | ||||
| 646 | return {}; | ||||
| 647 | default: | ||||
| 648 | errorf(pc() - 1, "unknown type form: %d", kind); | ||||
| 649 | return {}; | ||||
| 650 | } | ||||
| 651 | } | ||||
| 652 | |||||
| 653 | TypeDefinition consume_subtype_definition() { | ||||
| 654 | DCHECK(enabled_features_.has_gc())((void) 0); | ||||
| 655 | uint8_t kind = read_u8<Decoder::kFullValidation>(pc(), "type kind"); | ||||
| 656 | if (kind == kWasmSubtypeCode) { | ||||
| 657 | consume_bytes(1, "subtype definition"); | ||||
| 658 | constexpr uint32_t kMaximumSupertypes = 1; | ||||
| 659 | uint32_t supertype_count = | ||||
| 660 | consume_count("supertype count", kMaximumSupertypes); | ||||
| 661 | uint32_t supertype = | ||||
| 662 | supertype_count == 1 ? consume_u32v("supertype") : kNoSuperType; | ||||
| 663 | if (!check_supertype(supertype)) return {}; | ||||
| 664 | TypeDefinition type = consume_base_type_definition(); | ||||
| 665 | type.supertype = supertype; | ||||
| 666 | return type; | ||||
| 667 | } else { | ||||
| 668 | return consume_base_type_definition(); | ||||
| 669 | } | ||||
| 670 | } | ||||
| 671 | |||||
| 672 | void DecodeTypeSection() { | ||||
| 673 | TypeCanonicalizer* type_canon = GetTypeCanonicalizer(); | ||||
| 674 | uint32_t types_count = consume_count("types count", kV8MaxWasmTypes); | ||||
| 675 | |||||
| 676 | // Non wasm-gc type section decoding. | ||||
| 677 | if (!enabled_features_.has_gc()) { | ||||
| 678 | module_->types.reserve(types_count); | ||||
| 679 | for (uint32_t i = 0; i < types_count; ++i) { | ||||
| 680 | TRACE("DecodeSignature[%d] module+%d\n", i, | ||||
| 681 | static_cast<int>(pc_ - start_)); | ||||
| 682 | expect_u8("signature definition", kWasmFunctionTypeCode); | ||||
| 683 | const FunctionSig* sig = consume_sig(module_->signature_zone.get()); | ||||
| 684 | if (!ok()) break; | ||||
| 685 | module_->add_signature(sig, kNoSuperType); | ||||
| 686 | if (FLAG_wasm_type_canonicalization) { | ||||
| 687 | type_canon->AddRecursiveGroup(module_.get(), 1); | ||||
| 688 | } | ||||
| 689 | } | ||||
| 690 | return; | ||||
| 691 | } | ||||
| 692 | |||||
| 693 | if (types_count > 0) { | ||||
| 694 | uint8_t first_type_opcode = this->read_u8<Decoder::kFullValidation>(pc()); | ||||
| 695 | if (first_type_opcode == kWasmFunctionNominalCode || | ||||
| 696 | first_type_opcode == kWasmStructNominalCode || | ||||
| 697 | first_type_opcode == kWasmArrayNominalCode) { | ||||
| 698 | // wasm-gc nominal type section decoding. | ||||
| 699 | // In a nominal module, all types belong in the same recursive group. We | ||||
| 700 | // use the type vector's capacity to mark the end of the current | ||||
| 701 | // recursive group. | ||||
| 702 | module_->types.reserve(types_count); | ||||
| 703 | for (uint32_t i = 0; ok() && i
| ||||
| 704 | TRACE("DecodeType[%d] module+%d\n", i, | ||||
| 705 | static_cast<int>(pc_ - start_)); | ||||
| 706 | TypeDefinition type = consume_nominal_type_definition(); | ||||
| 707 | if (ok()) module_->add_type(type); | ||||
| 708 | } | ||||
| 709 | if (ok() && FLAG_wasm_type_canonicalization) { | ||||
| 710 | type_canon->AddRecursiveGroup(module_.get(), types_count); | ||||
| 711 | } | ||||
| 712 | } else { | ||||
| 713 | // wasm-gc isorecursive type section decoding. | ||||
| 714 | for (uint32_t i = 0; ok() && i < types_count; ++i) { | ||||
| 715 | TRACE("DecodeType[%d] module+%d\n", i, | ||||
| 716 | static_cast<int>(pc_ - start_)); | ||||
| 717 | uint8_t kind = read_u8<Decoder::kFullValidation>(pc(), "type kind"); | ||||
| 718 | if (kind == kWasmRecursiveTypeGroupCode) { | ||||
| 719 | consume_bytes(1, "rec. group definition"); | ||||
| 720 | uint32_t group_size = | ||||
| 721 | consume_count("recursive group size", kV8MaxWasmTypes); | ||||
| 722 | if (module_->types.size() + group_size > kV8MaxWasmTypes) { | ||||
| 723 | errorf(pc(), "Type definition count exeeds maximum %zu", | ||||
| 724 | kV8MaxWasmTypes); | ||||
| 725 | return; | ||||
| 726 | } | ||||
| 727 | // Reserve space for the current recursive group, so we are | ||||
| 728 | // allowed to reference its elements. | ||||
| 729 | module_->types.reserve(module_->types.size() + group_size); | ||||
| 730 | for (uint32_t i = 0; i < group_size; i++) { | ||||
| 731 | TypeDefinition type = consume_subtype_definition(); | ||||
| 732 | if (ok()) module_->add_type(type); | ||||
| 733 | } | ||||
| 734 | if (ok() && FLAG_wasm_type_canonicalization) { | ||||
| 735 | type_canon->AddRecursiveGroup(module_.get(), group_size); | ||||
| 736 | } | ||||
| 737 | } else { | ||||
| 738 | TypeDefinition type = consume_subtype_definition(); | ||||
| 739 | if (ok()) { | ||||
| 740 | module_->add_type(type); | ||||
| 741 | if (FLAG_wasm_type_canonicalization) { | ||||
| 742 | type_canon->AddRecursiveGroup(module_.get(), 1); | ||||
| 743 | } | ||||
| 744 | } | ||||
| 745 | } | ||||
| 746 | } | ||||
| 747 | } | ||||
| 748 | } | ||||
| 749 | |||||
| 750 | // Check validity of explicitly defined supertypes. | ||||
| 751 | const WasmModule* module = module_.get(); | ||||
| 752 | for (uint32_t i = 0; ok() && i < types_count; ++i) { | ||||
| 753 | uint32_t explicit_super = module_->supertype(i); | ||||
| 754 | if (explicit_super == kNoSuperType) continue; | ||||
| 755 | DCHECK_LT(explicit_super, types_count)((void) 0); // {consume_super_type} checks. | ||||
| 756 | int depth = GetSubtypingDepth(module, i); | ||||
| 757 | if (depth > static_cast<int>(kV8MaxRttSubtypingDepth)) { | ||||
| 758 | errorf("type %d: subtyping depth is greater than allowed", i); | ||||
| 759 | continue; | ||||
| 760 | } | ||||
| 761 | // TODO(7748): Replace this with a DCHECK once we reject inheritance | ||||
| 762 | // cycles for nominal modules. | ||||
| 763 | if (depth == -1) { | ||||
| 764 | errorf("type %d: cyclic inheritance", i); | ||||
| 765 | continue; | ||||
| 766 | } | ||||
| 767 | if (!ValidSubtypeDefinition(i, explicit_super, module, module)) { | ||||
| 768 | errorf("type %d has invalid explicit supertype %d", i, explicit_super); | ||||
| 769 | continue; | ||||
| 770 | } | ||||
| 771 | } | ||||
| 772 | module_->signature_map.Freeze(); | ||||
| 773 | } | ||||
| 774 | |||||
| 775 | void DecodeImportSection() { | ||||
| 776 | uint32_t import_table_count = | ||||
| 777 | consume_count("imports count", kV8MaxWasmImports); | ||||
| 778 | module_->import_table.reserve(import_table_count); | ||||
| 779 | for (uint32_t i = 0; ok() && i < import_table_count; ++i) { | ||||
| 780 | TRACE("DecodeImportTable[%d] module+%d\n", i, | ||||
| 781 | static_cast<int>(pc_ - start_)); | ||||
| 782 | |||||
| 783 | module_->import_table.push_back({ | ||||
| 784 | {0, 0}, // module_name | ||||
| 785 | {0, 0}, // field_name | ||||
| 786 | kExternalFunction, // kind | ||||
| 787 | 0 // index | ||||
| 788 | }); | ||||
| 789 | WasmImport* import = &module_->import_table.back(); | ||||
| 790 | const byte* pos = pc_; | ||||
| 791 | import->module_name = consume_string(this, true, "module name"); | ||||
| 792 | import->field_name = consume_string(this, true, "field name"); | ||||
| 793 | import->kind = | ||||
| 794 | static_cast<ImportExportKindCode>(consume_u8("import kind")); | ||||
| 795 | switch (import->kind) { | ||||
| 796 | case kExternalFunction: { | ||||
| 797 | // ===== Imported function =========================================== | ||||
| 798 | import->index = static_cast<uint32_t>(module_->functions.size()); | ||||
| 799 | module_->num_imported_functions++; | ||||
| 800 | module_->functions.push_back({nullptr, // sig | ||||
| 801 | import->index, // func_index | ||||
| 802 | 0, // sig_index | ||||
| 803 | {0, 0}, // code | ||||
| 804 | 0, // feedback slots | ||||
| 805 | true, // imported | ||||
| 806 | false, // exported | ||||
| 807 | false}); // declared | ||||
| 808 | WasmFunction* function = &module_->functions.back(); | ||||
| 809 | function->sig_index = | ||||
| 810 | consume_sig_index(module_.get(), &function->sig); | ||||
| 811 | break; | ||||
| 812 | } | ||||
| 813 | case kExternalTable: { | ||||
| 814 | // ===== Imported table ============================================== | ||||
| 815 | import->index = static_cast<uint32_t>(module_->tables.size()); | ||||
| 816 | module_->num_imported_tables++; | ||||
| 817 | module_->tables.emplace_back(); | ||||
| 818 | WasmTable* table = &module_->tables.back(); | ||||
| 819 | table->imported = true; | ||||
| 820 | const byte* type_position = pc(); | ||||
| 821 | ValueType type = consume_reference_type(); | ||||
| 822 | if (!WasmTable::IsValidTableType(type, module_.get())) { | ||||
| 823 | errorf(type_position, "Invalid table type %s", type.name().c_str()); | ||||
| 824 | break; | ||||
| 825 | } | ||||
| 826 | table->type = type; | ||||
| 827 | uint8_t flags = validate_table_flags("element count"); | ||||
| 828 | consume_resizable_limits( | ||||
| 829 | "element count", "elements", std::numeric_limits<uint32_t>::max(), | ||||
| 830 | &table->initial_size, &table->has_maximum_size, | ||||
| 831 | std::numeric_limits<uint32_t>::max(), &table->maximum_size, | ||||
| 832 | flags); | ||||
| 833 | break; | ||||
| 834 | } | ||||
| 835 | case kExternalMemory: { | ||||
| 836 | // ===== Imported memory ============================================= | ||||
| 837 | if (!AddMemory(module_.get())) break; | ||||
| 838 | uint8_t flags = validate_memory_flags(&module_->has_shared_memory, | ||||
| 839 | &module_->is_memory64); | ||||
| 840 | consume_resizable_limits( | ||||
| 841 | "memory", "pages", kSpecMaxMemoryPages, &module_->initial_pages, | ||||
| 842 | &module_->has_maximum_pages, kSpecMaxMemoryPages, | ||||
| 843 | &module_->maximum_pages, flags); | ||||
| 844 | break; | ||||
| 845 | } | ||||
| 846 | case kExternalGlobal: { | ||||
| 847 | // ===== Imported global ============================================= | ||||
| 848 | import->index = static_cast<uint32_t>(module_->globals.size()); | ||||
| 849 | module_->globals.push_back({kWasmVoid, false, {}, {0}, true, false}); | ||||
| 850 | WasmGlobal* global = &module_->globals.back(); | ||||
| 851 | global->type = consume_value_type(); | ||||
| 852 | global->mutability = consume_mutability(); | ||||
| 853 | if (global->mutability) { | ||||
| 854 | module_->num_imported_mutable_globals++; | ||||
| 855 | } | ||||
| 856 | break; | ||||
| 857 | } | ||||
| 858 | case kExternalTag: { | ||||
| 859 | // ===== Imported tag ================================================ | ||||
| 860 | if (!enabled_features_.has_eh()) { | ||||
| 861 | errorf(pos, "unknown import kind 0x%02x", import->kind); | ||||
| 862 | break; | ||||
| 863 | } | ||||
| 864 | import->index = static_cast<uint32_t>(module_->tags.size()); | ||||
| 865 | const WasmTagSig* tag_sig = nullptr; | ||||
| 866 | consume_exception_attribute(); // Attribute ignored for now. | ||||
| 867 | consume_tag_sig_index(module_.get(), &tag_sig); | ||||
| 868 | module_->tags.emplace_back(tag_sig); | ||||
| 869 | break; | ||||
| 870 | } | ||||
| 871 | default: | ||||
| 872 | errorf(pos, "unknown import kind 0x%02x", import->kind); | ||||
| 873 | break; | ||||
| 874 | } | ||||
| 875 | } | ||||
| 876 | } | ||||
| 877 | |||||
| 878 | void DecodeFunctionSection() { | ||||
| 879 | uint32_t functions_count = | ||||
| 880 | consume_count("functions count", kV8MaxWasmFunctions); | ||||
| 881 | auto counter = | ||||
| 882 | SELECT_WASM_COUNTER(GetCounters(), origin_, wasm_functions_per, module)((origin_) == kWasmOrigin ? (GetCounters())->wasm_functions_per_wasm_module () : (GetCounters())->wasm_functions_per_asm_module()); | ||||
| 883 | counter->AddSample(static_cast<int>(functions_count)); | ||||
| 884 | DCHECK_EQ(module_->functions.size(), module_->num_imported_functions)((void) 0); | ||||
| 885 | uint32_t total_function_count = | ||||
| 886 | module_->num_imported_functions + functions_count; | ||||
| 887 | module_->functions.reserve(total_function_count); | ||||
| 888 | module_->num_declared_functions = functions_count; | ||||
| 889 | for (uint32_t i = 0; i < functions_count; ++i) { | ||||
| 890 | uint32_t func_index = static_cast<uint32_t>(module_->functions.size()); | ||||
| 891 | module_->functions.push_back({nullptr, // sig | ||||
| 892 | func_index, // func_index | ||||
| 893 | 0, // sig_index | ||||
| 894 | {0, 0}, // code | ||||
| 895 | 0, // feedback slots | ||||
| 896 | false, // imported | ||||
| 897 | false, // exported | ||||
| 898 | false}); // declared | ||||
| 899 | WasmFunction* function = &module_->functions.back(); | ||||
| 900 | function->sig_index = consume_sig_index(module_.get(), &function->sig); | ||||
| 901 | if (!ok()) return; | ||||
| 902 | } | ||||
| 903 | DCHECK_EQ(module_->functions.size(), total_function_count)((void) 0); | ||||
| 904 | } | ||||
| 905 | |||||
| 906 | void DecodeTableSection() { | ||||
| 907 | uint32_t table_count = consume_count("table count", kV8MaxWasmTables); | ||||
| 908 | |||||
| 909 | for (uint32_t i = 0; ok() && i < table_count; i++) { | ||||
| 910 | module_->tables.emplace_back(); | ||||
| 911 | WasmTable* table = &module_->tables.back(); | ||||
| 912 | const byte* type_position = pc(); | ||||
| 913 | ValueType table_type = consume_reference_type(); | ||||
| 914 | if (!WasmTable::IsValidTableType(table_type, module_.get())) { | ||||
| 915 | error(type_position, | ||||
| 916 | "Currently, only externref and function references are allowed " | ||||
| 917 | "as table types"); | ||||
| 918 | continue; | ||||
| 919 | } | ||||
| 920 | table->type = table_type; | ||||
| 921 | uint8_t flags = validate_table_flags("table elements"); | ||||
| 922 | consume_resizable_limits( | ||||
| 923 | "table elements", "elements", std::numeric_limits<uint32_t>::max(), | ||||
| 924 | &table->initial_size, &table->has_maximum_size, | ||||
| 925 | std::numeric_limits<uint32_t>::max(), &table->maximum_size, flags); | ||||
| 926 | if (!table_type.is_defaultable()) { | ||||
| 927 | table->initial_value = consume_init_expr(module_.get(), table_type); | ||||
| 928 | } | ||||
| 929 | } | ||||
| 930 | } | ||||
| 931 | |||||
| 932 | void DecodeMemorySection() { | ||||
| 933 | uint32_t memory_count = consume_count("memory count", kV8MaxWasmMemories); | ||||
| 934 | |||||
| 935 | for (uint32_t i = 0; ok() && i < memory_count; i++) { | ||||
| 936 | if (!AddMemory(module_.get())) break; | ||||
| 937 | uint8_t flags = validate_memory_flags(&module_->has_shared_memory, | ||||
| 938 | &module_->is_memory64); | ||||
| 939 | consume_resizable_limits("memory", "pages", kSpecMaxMemoryPages, | ||||
| 940 | &module_->initial_pages, | ||||
| 941 | &module_->has_maximum_pages, kSpecMaxMemoryPages, | ||||
| 942 | &module_->maximum_pages, flags); | ||||
| 943 | } | ||||
| 944 | } | ||||
| 945 | |||||
| 946 | void DecodeGlobalSection() { | ||||
| 947 | uint32_t globals_count = consume_count("globals count", kV8MaxWasmGlobals); | ||||
| 948 | uint32_t imported_globals = static_cast<uint32_t>(module_->globals.size()); | ||||
| 949 | // It is important to not resize the globals vector from the beginning, | ||||
| 950 | // because we use its current size when decoding the initializer. | ||||
| 951 | module_->globals.reserve(imported_globals + globals_count); | ||||
| 952 | for (uint32_t i = 0; ok() && i < globals_count; ++i) { | ||||
| 953 | TRACE("DecodeGlobal[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); | ||||
| 954 | ValueType type = consume_value_type(); | ||||
| 955 | bool mutability = consume_mutability(); | ||||
| 956 | if (failed()) break; | ||||
| 957 | ConstantExpression init = consume_init_expr(module_.get(), type); | ||||
| 958 | module_->globals.push_back({type, mutability, init, {0}, false, false}); | ||||
| 959 | } | ||||
| 960 | if (ok()) CalculateGlobalOffsets(module_.get()); | ||||
| 961 | } | ||||
| 962 | |||||
| 963 | void DecodeExportSection() { | ||||
| 964 | uint32_t export_table_count = | ||||
| 965 | consume_count("exports count", kV8MaxWasmExports); | ||||
| 966 | module_->export_table.reserve(export_table_count); | ||||
| 967 | for (uint32_t i = 0; ok() && i < export_table_count; ++i) { | ||||
| 968 | TRACE("DecodeExportTable[%d] module+%d\n", i, | ||||
| 969 | static_cast<int>(pc_ - start_)); | ||||
| 970 | |||||
| 971 | module_->export_table.push_back({ | ||||
| 972 | {0, 0}, // name | ||||
| 973 | kExternalFunction, // kind | ||||
| 974 | 0 // index | ||||
| 975 | }); | ||||
| 976 | WasmExport* exp = &module_->export_table.back(); | ||||
| 977 | |||||
| 978 | exp->name = consume_string(this, true, "field name"); | ||||
| 979 | |||||
| 980 | const byte* pos = pc(); | ||||
| 981 | exp->kind = static_cast<ImportExportKindCode>(consume_u8("export kind")); | ||||
| 982 | switch (exp->kind) { | ||||
| 983 | case kExternalFunction: { | ||||
| 984 | WasmFunction* func = nullptr; | ||||
| 985 | exp->index = | ||||
| 986 | consume_func_index(module_.get(), &func, "export function index"); | ||||
| 987 | |||||
| 988 | if (failed()) break; | ||||
| 989 | DCHECK_NOT_NULL(func)((void) 0); | ||||
| 990 | |||||
| 991 | module_->num_exported_functions++; | ||||
| 992 | func->exported = true; | ||||
| 993 | // Exported functions are considered "declared". | ||||
| 994 | func->declared = true; | ||||
| 995 | break; | ||||
| 996 | } | ||||
| 997 | case kExternalTable: { | ||||
| 998 | WasmTable* table = nullptr; | ||||
| 999 | exp->index = consume_table_index(module_.get(), &table); | ||||
| 1000 | if (table) table->exported = true; | ||||
| 1001 | break; | ||||
| 1002 | } | ||||
| 1003 | case kExternalMemory: { | ||||
| 1004 | uint32_t index = consume_u32v("memory index"); | ||||
| 1005 | // TODO(titzer): This should become more regular | ||||
| 1006 | // once we support multiple memories. | ||||
| 1007 | if (!module_->has_memory || index != 0) { | ||||
| 1008 | error("invalid memory index != 0"); | ||||
| 1009 | } | ||||
| 1010 | module_->mem_export = true; | ||||
| 1011 | break; | ||||
| 1012 | } | ||||
| 1013 | case kExternalGlobal: { | ||||
| 1014 | WasmGlobal* global = nullptr; | ||||
| 1015 | exp->index = consume_global_index(module_.get(), &global); | ||||
| 1016 | if (global) { | ||||
| 1017 | global->exported = true; | ||||
| 1018 | } | ||||
| 1019 | break; | ||||
| 1020 | } | ||||
| 1021 | case kExternalTag: { | ||||
| 1022 | if (!enabled_features_.has_eh()) { | ||||
| 1023 | errorf(pos, "invalid export kind 0x%02x", exp->kind); | ||||
| 1024 | break; | ||||
| 1025 | } | ||||
| 1026 | WasmTag* tag = nullptr; | ||||
| 1027 | exp->index = consume_tag_index(module_.get(), &tag); | ||||
| 1028 | break; | ||||
| 1029 | } | ||||
| 1030 | default: | ||||
| 1031 | errorf(pos, "invalid export kind 0x%02x", exp->kind); | ||||
| 1032 | break; | ||||
| 1033 | } | ||||
| 1034 | } | ||||
| 1035 | // Check for duplicate exports (except for asm.js). | ||||
| 1036 | if (ok() && origin_ == kWasmOrigin && module_->export_table.size() > 1) { | ||||
| 1037 | std::vector<WasmExport> sorted_exports(module_->export_table); | ||||
| 1038 | |||||
| 1039 | auto cmp_less = [this](const WasmExport& a, const WasmExport& b) { | ||||
| 1040 | // Return true if a < b. | ||||
| 1041 | if (a.name.length() != b.name.length()) { | ||||
| 1042 | return a.name.length() < b.name.length(); | ||||
| 1043 | } | ||||
| 1044 | const byte* left = start() + GetBufferRelativeOffset(a.name.offset()); | ||||
| 1045 | const byte* right = start() + GetBufferRelativeOffset(b.name.offset()); | ||||
| 1046 | return memcmp(left, right, a.name.length()) < 0; | ||||
| 1047 | }; | ||||
| 1048 | std::stable_sort(sorted_exports.begin(), sorted_exports.end(), cmp_less); | ||||
| 1049 | |||||
| 1050 | auto it = sorted_exports.begin(); | ||||
| 1051 | WasmExport* last = &*it++; | ||||
| 1052 | for (auto end = sorted_exports.end(); it != end; last = &*it++) { | ||||
| 1053 | DCHECK(!cmp_less(*it, *last))((void) 0); // Vector must be sorted. | ||||
| 1054 | if (!cmp_less(*last, *it)) { | ||||
| 1055 | const byte* pc = start() + GetBufferRelativeOffset(it->name.offset()); | ||||
| 1056 | TruncatedUserString<> name(pc, it->name.length()); | ||||
| 1057 | errorf(pc, "Duplicate export name '%.*s' for %s %d and %s %d", | ||||
| 1058 | name.length(), name.start(), ExternalKindName(last->kind), | ||||
| 1059 | last->index, ExternalKindName(it->kind), it->index); | ||||
| 1060 | break; | ||||
| 1061 | } | ||||
| 1062 | } | ||||
| 1063 | } | ||||
| 1064 | } | ||||
| 1065 | |||||
| 1066 | void DecodeStartSection() { | ||||
| 1067 | WasmFunction* func; | ||||
| 1068 | const byte* pos = pc_; | ||||
| 1069 | module_->start_function_index = | ||||
| 1070 | consume_func_index(module_.get(), &func, "start function index"); | ||||
| 1071 | if (func && | ||||
| 1072 | (func->sig->parameter_count() > 0 || func->sig->return_count() > 0)) { | ||||
| 1073 | error(pos, "invalid start function: non-zero parameter or return count"); | ||||
| 1074 | } | ||||
| 1075 | } | ||||
| 1076 | |||||
| 1077 | void DecodeElementSection() { | ||||
| 1078 | uint32_t element_count = | ||||
| 1079 | consume_count("element count", FLAG_wasm_max_table_size); | ||||
| 1080 | |||||
| 1081 | for (uint32_t i = 0; i < element_count; ++i) { | ||||
| 1082 | WasmElemSegment segment = consume_element_segment_header(); | ||||
| 1083 | if (failed()) return; | ||||
| 1084 | DCHECK_NE(segment.type, kWasmBottom)((void) 0); | ||||
| 1085 | |||||
| 1086 | uint32_t num_elem = | ||||
| 1087 | consume_count("number of elements", max_table_init_entries()); | ||||
| 1088 | |||||
| 1089 | for (uint32_t j = 0; j < num_elem; j++) { | ||||
| 1090 | ConstantExpression entry = | ||||
| 1091 | segment.element_type == WasmElemSegment::kExpressionElements | ||||
| 1092 | ? consume_init_expr(module_.get(), segment.type) | ||||
| 1093 | : ConstantExpression::RefFunc( | ||||
| 1094 | consume_element_func_index(segment.type)); | ||||
| 1095 | if (failed()) return; | ||||
| 1096 | segment.entries.push_back(entry); | ||||
| 1097 | } | ||||
| 1098 | module_->elem_segments.push_back(std::move(segment)); | ||||
| 1099 | } | ||||
| 1100 | } | ||||
| 1101 | |||||
| 1102 | void DecodeCodeSection(bool verify_functions) { | ||||
| 1103 | StartCodeSection(); | ||||
| 1104 | uint32_t code_section_start = pc_offset(); | ||||
| 1105 | uint32_t functions_count = consume_u32v("functions count"); | ||||
| 1106 | CheckFunctionsCount(functions_count, code_section_start); | ||||
| 1107 | for (uint32_t i = 0; ok() && i < functions_count; ++i) { | ||||
| 1108 | const byte* pos = pc(); | ||||
| 1109 | uint32_t size = consume_u32v("body size"); | ||||
| 1110 | if (size > kV8MaxWasmFunctionSize) { | ||||
| 1111 | errorf(pos, "size %u > maximum function size %zu", size, | ||||
| 1112 | kV8MaxWasmFunctionSize); | ||||
| 1113 | return; | ||||
| 1114 | } | ||||
| 1115 | uint32_t offset = pc_offset(); | ||||
| 1116 | consume_bytes(size, "function body"); | ||||
| 1117 | if (failed()) break; | ||||
| 1118 | DecodeFunctionBody(i, size, offset, verify_functions); | ||||
| 1119 | } | ||||
| 1120 | DCHECK_GE(pc_offset(), code_section_start)((void) 0); | ||||
| 1121 | set_code_section(code_section_start, pc_offset() - code_section_start); | ||||
| 1122 | } | ||||
| 1123 | |||||
| 1124 | void StartCodeSection() { | ||||
| 1125 | if (ok()) { | ||||
| 1126 | // Make sure global offset were calculated before they get accessed during | ||||
| 1127 | // function compilation. | ||||
| 1128 | CalculateGlobalOffsets(module_.get()); | ||||
| 1129 | } | ||||
| 1130 | } | ||||
| 1131 | |||||
| 1132 | bool CheckFunctionsCount(uint32_t functions_count, uint32_t error_offset) { | ||||
| 1133 | if (functions_count != module_->num_declared_functions) { | ||||
| 1134 | errorf(error_offset, "function body count %u mismatch (%u expected)", | ||||
| 1135 | functions_count, module_->num_declared_functions); | ||||
| 1136 | return false; | ||||
| 1137 | } | ||||
| 1138 | return true; | ||||
| 1139 | } | ||||
| 1140 | |||||
| 1141 | void DecodeFunctionBody(uint32_t index, uint32_t length, uint32_t offset, | ||||
| 1142 | bool verify_functions) { | ||||
| 1143 | WasmFunction* function = | ||||
| 1144 | &module_->functions[index + module_->num_imported_functions]; | ||||
| 1145 | function->code = {offset, length}; | ||||
| 1146 | if (verify_functions) { | ||||
| 1147 | ModuleWireBytes bytes(module_start_, module_end_); | ||||
| 1148 | VerifyFunctionBody(module_->signature_zone->allocator(), | ||||
| 1149 | index + module_->num_imported_functions, bytes, | ||||
| 1150 | module_.get(), function); | ||||
| 1151 | } | ||||
| 1152 | } | ||||
| 1153 | |||||
| 1154 | bool CheckDataSegmentsCount(uint32_t data_segments_count) { | ||||
| 1155 | if (has_seen_unordered_section(kDataCountSectionCode) && | ||||
| 1156 | data_segments_count != module_->num_declared_data_segments) { | ||||
| 1157 | errorf(pc(), "data segments count %u mismatch (%u expected)", | ||||
| 1158 | data_segments_count, module_->num_declared_data_segments); | ||||
| 1159 | return false; | ||||
| 1160 | } | ||||
| 1161 | return true; | ||||
| 1162 | } | ||||
| 1163 | |||||
| 1164 | void DecodeDataSection() { | ||||
| 1165 | uint32_t data_segments_count = | ||||
| 1166 | consume_count("data segments count", kV8MaxWasmDataSegments); | ||||
| 1167 | if (!CheckDataSegmentsCount(data_segments_count)) return; | ||||
| 1168 | |||||
| 1169 | module_->data_segments.reserve(data_segments_count); | ||||
| 1170 | for (uint32_t i = 0; ok() && i < data_segments_count; ++i) { | ||||
| 1171 | const byte* pos = pc(); | ||||
| 1172 | TRACE("DecodeDataSegment[%d] module+%d\n", i, | ||||
| 1173 | static_cast<int>(pc_ - start_)); | ||||
| 1174 | |||||
| 1175 | bool is_active; | ||||
| 1176 | uint32_t memory_index; | ||||
| 1177 | ConstantExpression dest_addr; | ||||
| 1178 | consume_data_segment_header(&is_active, &memory_index, &dest_addr); | ||||
| 1179 | if (failed()) break; | ||||
| 1180 | |||||
| 1181 | if (is_active) { | ||||
| 1182 | if (!module_->has_memory) { | ||||
| 1183 | error("cannot load data without memory"); | ||||
| 1184 | break; | ||||
| 1185 | } | ||||
| 1186 | if (memory_index != 0) { | ||||
| 1187 | errorf(pos, "illegal memory index %u != 0", memory_index); | ||||
| 1188 | break; | ||||
| 1189 | } | ||||
| 1190 | } | ||||
| 1191 | |||||
| 1192 | uint32_t source_length = consume_u32v("source size"); | ||||
| 1193 | uint32_t source_offset = pc_offset(); | ||||
| 1194 | |||||
| 1195 | if (is_active) { | ||||
| 1196 | module_->data_segments.emplace_back(std::move(dest_addr)); | ||||
| 1197 | } else { | ||||
| 1198 | module_->data_segments.emplace_back(); | ||||
| 1199 | } | ||||
| 1200 | |||||
| 1201 | WasmDataSegment* segment = &module_->data_segments.back(); | ||||
| 1202 | |||||
| 1203 | consume_bytes(source_length, "segment data"); | ||||
| 1204 | if (failed()) break; | ||||
| 1205 | |||||
| 1206 | segment->source = {source_offset, source_length}; | ||||
| 1207 | } | ||||
| 1208 | } | ||||
| 1209 | |||||
| 1210 | void DecodeNameSection() { | ||||
| 1211 | // TODO(titzer): find a way to report name errors as warnings. | ||||
| 1212 | // Ignore all but the first occurrence of name section. | ||||
| 1213 | if (!has_seen_unordered_section(kNameSectionCode)) { | ||||
| 1214 | set_seen_unordered_section(kNameSectionCode); | ||||
| 1215 | // Use an inner decoder so that errors don't fail the outer decoder. | ||||
| 1216 | Decoder inner(start_, pc_, end_, buffer_offset_); | ||||
| 1217 | // Decode all name subsections. | ||||
| 1218 | // Be lenient with their order. | ||||
| 1219 | while (inner.ok() && inner.more()) { | ||||
| 1220 | uint8_t name_type = inner.consume_u8("name type"); | ||||
| 1221 | if (name_type & 0x80) inner.error("name type if not varuint7"); | ||||
| 1222 | |||||
| 1223 | uint32_t name_payload_len = inner.consume_u32v("name payload length"); | ||||
| 1224 | if (!inner.checkAvailable(name_payload_len)) break; | ||||
| 1225 | |||||
| 1226 | // Decode module name, ignore the rest. | ||||
| 1227 | // Function and local names will be decoded when needed. | ||||
| 1228 | if (name_type == NameSectionKindCode::kModuleCode) { | ||||
| 1229 | WireBytesRef name = consume_string(&inner, false, "module name"); | ||||
| 1230 | if (inner.ok() && validate_utf8(&inner, name)) { | ||||
| 1231 | module_->name = name; | ||||
| 1232 | } | ||||
| 1233 | } else { | ||||
| 1234 | inner.consume_bytes(name_payload_len, "name subsection payload"); | ||||
| 1235 | } | ||||
| 1236 | } | ||||
| 1237 | } | ||||
| 1238 | // Skip the whole names section in the outer decoder. | ||||
| 1239 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
| 1240 | } | ||||
| 1241 | |||||
| 1242 | void DecodeSourceMappingURLSection() { | ||||
| 1243 | Decoder inner(start_, pc_, end_, buffer_offset_); | ||||
| 1244 | WireBytesRef url = wasm::consume_string(&inner, true, "module name"); | ||||
| 1245 | if (inner.ok() && | ||||
| 1246 | module_->debug_symbols.type != WasmDebugSymbols::Type::SourceMap) { | ||||
| 1247 | module_->debug_symbols = {WasmDebugSymbols::Type::SourceMap, url}; | ||||
| 1248 | } | ||||
| 1249 | set_seen_unordered_section(kSourceMappingURLSectionCode); | ||||
| 1250 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
| 1251 | } | ||||
| 1252 | |||||
| 1253 | void DecodeExternalDebugInfoSection() { | ||||
| 1254 | Decoder inner(start_, pc_, end_, buffer_offset_); | ||||
| 1255 | WireBytesRef url = | ||||
| 1256 | wasm::consume_string(&inner, true, "external symbol file"); | ||||
| 1257 | // If there is an explicit source map, prefer it over DWARF info. | ||||
| 1258 | if (inner.ok() && | ||||
| 1259 | module_->debug_symbols.type != WasmDebugSymbols::Type::SourceMap) { | ||||
| 1260 | module_->debug_symbols = {WasmDebugSymbols::Type::ExternalDWARF, url}; | ||||
| 1261 | set_seen_unordered_section(kExternalDebugInfoSectionCode); | ||||
| 1262 | } | ||||
| 1263 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
| 1264 | } | ||||
| 1265 | |||||
| 1266 | void DecodeCompilationHintsSection() { | ||||
| 1267 | TRACE("DecodeCompilationHints module+%d\n", static_cast<int>(pc_ - start_)); | ||||
| 1268 | |||||
| 1269 | // TODO(frgossen): Find a way to report compilation hint errors as warnings. | ||||
| 1270 | // All except first occurrence after function section and before code | ||||
| 1271 | // section are ignored. | ||||
| 1272 | const bool before_function_section = | ||||
| 1273 | next_ordered_section_ <= kFunctionSectionCode; | ||||
| 1274 | const bool after_code_section = next_ordered_section_ > kCodeSectionCode; | ||||
| 1275 | if (before_function_section || after_code_section || | ||||
| 1276 | has_seen_unordered_section(kCompilationHintsSectionCode)) { | ||||
| 1277 | return; | ||||
| 1278 | } | ||||
| 1279 | set_seen_unordered_section(kCompilationHintsSectionCode); | ||||
| 1280 | |||||
| 1281 | // TODO(frgossen) Propagate errors to outer decoder in experimental phase. | ||||
| 1282 | // We should use an inner decoder later and propagate its errors as | ||||
| 1283 | // warnings. | ||||
| 1284 | Decoder& decoder = *this; | ||||
| 1285 | // Decoder decoder(start_, pc_, end_, buffer_offset_); | ||||
| 1286 | |||||
| 1287 | // Ensure exactly one compilation hint per function. | ||||
| 1288 | uint32_t hint_count = decoder.consume_u32v("compilation hint count"); | ||||
| 1289 | if (hint_count != module_->num_declared_functions) { | ||||
| 1290 | decoder.errorf(decoder.pc(), "Expected %u compilation hints (%u found)", | ||||
| 1291 | module_->num_declared_functions, hint_count); | ||||
| 1292 | } | ||||
| 1293 | |||||
| 1294 | // Decode sequence of compilation hints. | ||||
| 1295 | if (decoder.ok()) { | ||||
| 1296 | module_->compilation_hints.reserve(hint_count); | ||||
| 1297 | } | ||||
| 1298 | for (uint32_t i = 0; decoder.ok() && i < hint_count; i++) { | ||||
| 1299 | TRACE("DecodeCompilationHints[%d] module+%d\n", i, | ||||
| 1300 | static_cast<int>(pc_ - start_)); | ||||
| 1301 | |||||
| 1302 | // Compilation hints are encoded in one byte each. | ||||
| 1303 | // +-------+----------+---------------+----------+ | ||||
| 1304 | // | 2 bit | 2 bit | 2 bit | 2 bit | | ||||
| 1305 | // | ... | Top tier | Baseline tier | Strategy | | ||||
| 1306 | // +-------+----------+---------------+----------+ | ||||
| 1307 | uint8_t hint_byte = decoder.consume_u8("compilation hint"); | ||||
| 1308 | if (!decoder.ok()) break; | ||||
| 1309 | |||||
| 1310 | // Validate the hint_byte. | ||||
| 1311 | // For the compilation strategy, all 2-bit values are valid. For the tier, | ||||
| 1312 | // only 0x0, 0x1, and 0x2 are allowed. | ||||
| 1313 | static_assert( | ||||
| 1314 | static_cast<int>(WasmCompilationHintTier::kDefault) == 0 && | ||||
| 1315 | static_cast<int>(WasmCompilationHintTier::kBaseline) == 1 && | ||||
| 1316 | static_cast<int>(WasmCompilationHintTier::kOptimized) == 2, | ||||
| 1317 | "The check below assumes that 0x03 is the only invalid 2-bit number " | ||||
| 1318 | "for a compilation tier"); | ||||
| 1319 | if (((hint_byte >> 2) & 0x03) == 0x03 || | ||||
| 1320 | ((hint_byte >> 4) & 0x03) == 0x03) { | ||||
| 1321 | decoder.errorf(decoder.pc(), | ||||
| 1322 | "Invalid compilation hint %#04x (invalid tier 0x03)", | ||||
| 1323 | hint_byte); | ||||
| 1324 | break; | ||||
| 1325 | } | ||||
| 1326 | |||||
| 1327 | // Decode compilation hint. | ||||
| 1328 | WasmCompilationHint hint; | ||||
| 1329 | hint.strategy = | ||||
| 1330 | static_cast<WasmCompilationHintStrategy>(hint_byte & 0x03); | ||||
| 1331 | hint.baseline_tier = | ||||
| 1332 | static_cast<WasmCompilationHintTier>((hint_byte >> 2) & 0x03); | ||||
| 1333 | hint.top_tier = | ||||
| 1334 | static_cast<WasmCompilationHintTier>((hint_byte >> 4) & 0x03); | ||||
| 1335 | |||||
| 1336 | // Ensure that the top tier never downgrades a compilation result. If | ||||
| 1337 | // baseline and top tier are the same compilation will be invoked only | ||||
| 1338 | // once. | ||||
| 1339 | if (hint.top_tier < hint.baseline_tier && | ||||
| 1340 | hint.top_tier != WasmCompilationHintTier::kDefault) { | ||||
| 1341 | decoder.errorf(decoder.pc(), | ||||
| 1342 | "Invalid compilation hint %#04x (forbidden downgrade)", | ||||
| 1343 | hint_byte); | ||||
| 1344 | } | ||||
| 1345 | |||||
| 1346 | // Happily accept compilation hint. | ||||
| 1347 | if (decoder.ok()) { | ||||
| 1348 | module_->compilation_hints.push_back(std::move(hint)); | ||||
| 1349 | } | ||||
| 1350 | } | ||||
| 1351 | |||||
| 1352 | // If section was invalid reset compilation hints. | ||||
| 1353 | if (decoder.failed()) { | ||||
| 1354 | module_->compilation_hints.clear(); | ||||
| 1355 | } | ||||
| 1356 | |||||
| 1357 | // @TODO(frgossen) Skip the whole compilation hints section in the outer | ||||
| 1358 | // decoder if inner decoder was used. | ||||
| 1359 | // consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
| 1360 | } | ||||
| 1361 | |||||
| 1362 | void DecodeBranchHintsSection() { | ||||
| 1363 | TRACE("DecodeBranchHints module+%d\n", static_cast<int>(pc_ - start_)); | ||||
| 1364 | if (!has_seen_unordered_section(kBranchHintsSectionCode)) { | ||||
| 1365 | set_seen_unordered_section(kBranchHintsSectionCode); | ||||
| 1366 | // Use an inner decoder so that errors don't fail the outer decoder. | ||||
| 1367 | Decoder inner(start_, pc_, end_, buffer_offset_); | ||||
| 1368 | BranchHintInfo branch_hints; | ||||
| 1369 | |||||
| 1370 | uint32_t func_count = inner.consume_u32v("number of functions"); | ||||
| 1371 | // Keep track of the previous function index to validate the ordering | ||||
| 1372 | int64_t last_func_idx = -1; | ||||
| 1373 | for (uint32_t i = 0; i < func_count; i++) { | ||||
| 1374 | uint32_t func_idx = inner.consume_u32v("function index"); | ||||
| 1375 | if (int64_t(func_idx) <= last_func_idx) { | ||||
| 1376 | inner.errorf("Invalid function index: %d", func_idx); | ||||
| 1377 | break; | ||||
| 1378 | } | ||||
| 1379 | last_func_idx = func_idx; | ||||
| 1380 | uint32_t num_hints = inner.consume_u32v("number of hints"); | ||||
| 1381 | BranchHintMap func_branch_hints; | ||||
| 1382 | TRACE("DecodeBranchHints[%d] module+%d\n", func_idx, | ||||
| 1383 | static_cast<int>(inner.pc() - inner.start())); | ||||
| 1384 | // Keep track of the previous branch offset to validate the ordering | ||||
| 1385 | int64_t last_br_off = -1; | ||||
| 1386 | for (uint32_t j = 0; j < num_hints; ++j) { | ||||
| 1387 | uint32_t br_off = inner.consume_u32v("branch instruction offset"); | ||||
| 1388 | if (int64_t(br_off) <= last_br_off) { | ||||
| 1389 | inner.errorf("Invalid branch offset: %d", br_off); | ||||
| 1390 | break; | ||||
| 1391 | } | ||||
| 1392 | last_br_off = br_off; | ||||
| 1393 | uint32_t data_size = inner.consume_u32v("data size"); | ||||
| 1394 | if (data_size != 1) { | ||||
| 1395 | inner.errorf("Invalid data size: %#x. Expected 1.", data_size); | ||||
| 1396 | break; | ||||
| 1397 | } | ||||
| 1398 | uint32_t br_dir = inner.consume_u8("branch direction"); | ||||
| 1399 | TRACE("DecodeBranchHints[%d][%d] module+%d\n", func_idx, br_off, | ||||
| 1400 | static_cast<int>(inner.pc() - inner.start())); | ||||
| 1401 | WasmBranchHint hint; | ||||
| 1402 | switch (br_dir) { | ||||
| 1403 | case 0: | ||||
| 1404 | hint = WasmBranchHint::kUnlikely; | ||||
| 1405 | break; | ||||
| 1406 | case 1: | ||||
| 1407 | hint = WasmBranchHint::kLikely; | ||||
| 1408 | break; | ||||
| 1409 | default: | ||||
| 1410 | hint = WasmBranchHint::kNoHint; | ||||
| 1411 | inner.errorf(inner.pc(), "Invalid branch hint %#x", br_dir); | ||||
| 1412 | break; | ||||
| 1413 | } | ||||
| 1414 | if (!inner.ok()) { | ||||
| 1415 | break; | ||||
| 1416 | } | ||||
| 1417 | func_branch_hints.insert(br_off, hint); | ||||
| 1418 | } | ||||
| 1419 | if (!inner.ok()) { | ||||
| 1420 | break; | ||||
| 1421 | } | ||||
| 1422 | branch_hints.emplace(func_idx, std::move(func_branch_hints)); | ||||
| 1423 | } | ||||
| 1424 | // Extra unexpected bytes are an error. | ||||
| 1425 | if (inner.more()) { | ||||
| 1426 | inner.errorf("Unexpected extra bytes: %d\n", | ||||
| 1427 | static_cast<int>(inner.pc() - inner.start())); | ||||
| 1428 | } | ||||
| 1429 | // If everything went well, accept the hints for the module. | ||||
| 1430 | if (inner.ok()) { | ||||
| 1431 | module_->branch_hints = std::move(branch_hints); | ||||
| 1432 | } | ||||
| 1433 | } | ||||
| 1434 | // Skip the whole branch hints section in the outer decoder. | ||||
| 1435 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
| 1436 | } | ||||
| 1437 | |||||
| 1438 | void DecodeDataCountSection() { | ||||
| 1439 | module_->num_declared_data_segments = | ||||
| 1440 | consume_count("data segments count", kV8MaxWasmDataSegments); | ||||
| 1441 | } | ||||
| 1442 | |||||
| 1443 | void DecodeTagSection() { | ||||
| 1444 | uint32_t tag_count = consume_count("tag count", kV8MaxWasmTags); | ||||
| 1445 | for (uint32_t i = 0; ok() && i < tag_count; ++i) { | ||||
| 1446 | TRACE("DecodeTag[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); | ||||
| 1447 | const WasmTagSig* tag_sig = nullptr; | ||||
| 1448 | consume_exception_attribute(); // Attribute ignored for now. | ||||
| 1449 | consume_tag_sig_index(module_.get(), &tag_sig); | ||||
| 1450 | module_->tags.emplace_back(tag_sig); | ||||
| 1451 | } | ||||
| 1452 | } | ||||
| 1453 | |||||
| 1454 | bool CheckMismatchedCounts() { | ||||
| 1455 | // The declared vs. defined function count is normally checked when | ||||
| 1456 | // decoding the code section, but we have to check it here too in case the | ||||
| 1457 | // code section is absent. | ||||
| 1458 | if (module_->num_declared_functions != 0) { | ||||
| 1459 | DCHECK_LT(module_->num_imported_functions, module_->functions.size())((void) 0); | ||||
| 1460 | // We know that the code section has been decoded if the first | ||||
| 1461 | // non-imported function has its code set. | ||||
| 1462 | if (!module_->functions[module_->num_imported_functions].code.is_set()) { | ||||
| 1463 | errorf(pc(), "function count is %u, but code section is absent", | ||||
| 1464 | module_->num_declared_functions); | ||||
| 1465 | return false; | ||||
| 1466 | } | ||||
| 1467 | } | ||||
| 1468 | // Perform a similar check for the DataCount and Data sections, where data | ||||
| 1469 | // segments are declared but the Data section is absent. | ||||
| 1470 | if (!CheckDataSegmentsCount( | ||||
| 1471 | static_cast<uint32_t>(module_->data_segments.size()))) { | ||||
| 1472 | return false; | ||||
| 1473 | } | ||||
| 1474 | return true; | ||||
| 1475 | } | ||||
| 1476 | |||||
| 1477 | ModuleResult FinishDecoding(bool verify_functions = true) { | ||||
| 1478 | if (ok() && CheckMismatchedCounts()) { | ||||
| 1479 | // We calculate the global offsets here, because there may not be a | ||||
| 1480 | // global section and code section that would have triggered the | ||||
| 1481 | // calculation before. Even without the globals section the calculation | ||||
| 1482 | // is needed because globals can also be defined in the import section. | ||||
| 1483 | CalculateGlobalOffsets(module_.get()); | ||||
| 1484 | } | ||||
| 1485 | |||||
| 1486 | ModuleResult result = toResult(std::move(module_)); | ||||
| 1487 | if (verify_functions && result.ok() && intermediate_error_.has_error()) { | ||||
| 1488 | // Copy error message and location. | ||||
| 1489 | return ModuleResult{std::move(intermediate_error_)}; | ||||
| 1490 | } | ||||
| 1491 | return result; | ||||
| 1492 | } | ||||
| 1493 | |||||
| 1494 | void set_code_section(uint32_t offset, uint32_t size) { | ||||
| 1495 | module_->code = {offset, size}; | ||||
| 1496 | } | ||||
| 1497 | |||||
| 1498 | // Decodes an entire module. | ||||
| 1499 | ModuleResult DecodeModule(Counters* counters, AccountingAllocator* allocator, | ||||
| 1500 | bool verify_functions = true) { | ||||
| 1501 | StartDecoding(counters, allocator); | ||||
| 1502 | uint32_t offset = 0; | ||||
| 1503 | base::Vector<const byte> orig_bytes(start(), end() - start()); | ||||
| 1504 | DecodeModuleHeader(base::VectorOf(start(), end() - start()), offset); | ||||
| 1505 | if (failed()) { | ||||
| 1506 | return FinishDecoding(verify_functions); | ||||
| 1507 | } | ||||
| 1508 | // Size of the module header. | ||||
| 1509 | offset += 8; | ||||
| 1510 | Decoder decoder(start_ + offset, end_, offset); | ||||
| 1511 | |||||
| 1512 | WasmSectionIterator section_iter(&decoder); | ||||
| 1513 | |||||
| 1514 | while (ok()) { | ||||
| 1515 | // Shift the offset by the section header length | ||||
| 1516 | offset += section_iter.payload_start() - section_iter.section_start(); | ||||
| 1517 | if (section_iter.section_code() != SectionCode::kUnknownSectionCode) { | ||||
| 1518 | DecodeSection(section_iter.section_code(), section_iter.payload(), | ||||
| 1519 | offset, verify_functions); | ||||
| 1520 | } | ||||
| 1521 | // Shift the offset by the remaining section payload | ||||
| 1522 | offset += section_iter.payload_length(); | ||||
| 1523 | if (!section_iter.more()) break; | ||||
| 1524 | section_iter.advance(true); | ||||
| 1525 | } | ||||
| 1526 | |||||
| 1527 | if (FLAG_dump_wasm_module) DumpModule(orig_bytes); | ||||
| 1528 | |||||
| 1529 | if (decoder.failed()) { | ||||
| 1530 | return decoder.toResult<std::unique_ptr<WasmModule>>(nullptr); | ||||
| 1531 | } | ||||
| 1532 | |||||
| 1533 | return FinishDecoding(verify_functions); | ||||
| 1534 | } | ||||
| 1535 | |||||
| 1536 | // Decodes a single anonymous function starting at {start_}. | ||||
| 1537 | FunctionResult DecodeSingleFunction(Zone* zone, | ||||
| 1538 | const ModuleWireBytes& wire_bytes, | ||||
| 1539 | const WasmModule* module, | ||||
| 1540 | std::unique_ptr<WasmFunction> function) { | ||||
| 1541 | pc_ = start_; | ||||
| 1542 | expect_u8("type form", kWasmFunctionTypeCode); | ||||
| 1543 | if (!ok()) return FunctionResult{std::move(intermediate_error_)}; | ||||
| 1544 | function->sig = consume_sig(zone); | ||||
| 1545 | function->code = {off(pc_), static_cast<uint32_t>(end_ - pc_)}; | ||||
| 1546 | |||||
| 1547 | if (ok()) | ||||
| 1548 | VerifyFunctionBody(zone->allocator(), 0, wire_bytes, module, | ||||
| 1549 | function.get()); | ||||
| 1550 | |||||
| 1551 | if (intermediate_error_.has_error()) { | ||||
| 1552 | return FunctionResult{std::move(intermediate_error_)}; | ||||
| 1553 | } | ||||
| 1554 | |||||
| 1555 | return FunctionResult(std::move(function)); | ||||
| 1556 | } | ||||
| 1557 | |||||
| 1558 | // Decodes a single function signature at {start}. | ||||
| 1559 | const FunctionSig* DecodeFunctionSignature(Zone* zone, const byte* start) { | ||||
| 1560 | pc_ = start; | ||||
| 1561 | if (!expect_u8("type form", kWasmFunctionTypeCode)) return nullptr; | ||||
| 1562 | const FunctionSig* result = consume_sig(zone); | ||||
| 1563 | return ok() ? result : nullptr; | ||||
| 1564 | } | ||||
| 1565 | |||||
| 1566 | ConstantExpression DecodeInitExprForTesting(ValueType expected) { | ||||
| 1567 | return consume_init_expr(module_.get(), expected); | ||||
| 1568 | } | ||||
| 1569 | |||||
| 1570 | const std::shared_ptr<WasmModule>& shared_module() const { return module_; } | ||||
| 1571 | |||||
| 1572 | Counters* GetCounters() const { | ||||
| 1573 | DCHECK_NOT_NULL(counters_)((void) 0); | ||||
| 1574 | return counters_; | ||||
| 1575 | } | ||||
| 1576 | |||||
| 1577 | void SetCounters(Counters* counters) { | ||||
| 1578 | DCHECK_NULL(counters_)((void) 0); | ||||
| 1579 | counters_ = counters; | ||||
| 1580 | } | ||||
| 1581 | |||||
| 1582 | private: | ||||
| 1583 | const WasmFeatures enabled_features_; | ||||
| 1584 | std::shared_ptr<WasmModule> module_; | ||||
| 1585 | const byte* module_start_ = nullptr; | ||||
| 1586 | const byte* module_end_ = nullptr; | ||||
| 1587 | Counters* counters_ = nullptr; | ||||
| 1588 | // The type section is the first section in a module. | ||||
| 1589 | uint8_t next_ordered_section_ = kFirstSectionInModule; | ||||
| 1590 | // We store next_ordered_section_ as uint8_t instead of SectionCode so that | ||||
| 1591 | // we can increment it. This static_assert should make sure that SectionCode | ||||
| 1592 | // does not get bigger than uint8_t accidentially. | ||||
| 1593 | static_assert(sizeof(ModuleDecoderImpl::next_ordered_section_) == | ||||
| 1594 | sizeof(SectionCode), | ||||
| 1595 | "type mismatch"); | ||||
| 1596 | uint32_t seen_unordered_sections_ = 0; | ||||
| 1597 | static_assert(kBitsPerByte * | ||||
| 1598 | sizeof(ModuleDecoderImpl::seen_unordered_sections_) > | ||||
| 1599 | kLastKnownModuleSection, | ||||
| 1600 | "not enough bits"); | ||||
| 1601 | WasmError intermediate_error_; | ||||
| 1602 | ModuleOrigin origin_; | ||||
| 1603 | AccountingAllocator allocator_; | ||||
| 1604 | Zone init_expr_zone_{&allocator_, "initializer expression zone"}; | ||||
| 1605 | |||||
| 1606 | bool has_seen_unordered_section(SectionCode section_code) { | ||||
| 1607 | return seen_unordered_sections_ & (1 << section_code); | ||||
| 1608 | } | ||||
| 1609 | |||||
| 1610 | void set_seen_unordered_section(SectionCode section_code) { | ||||
| 1611 | seen_unordered_sections_ |= 1 << section_code; | ||||
| 1612 | } | ||||
| 1613 | |||||
| 1614 | uint32_t off(const byte* ptr) { | ||||
| 1615 | return static_cast<uint32_t>(ptr - start_) + buffer_offset_; | ||||
| 1616 | } | ||||
| 1617 | |||||
| 1618 | bool AddMemory(WasmModule* module) { | ||||
| 1619 | if (module->has_memory) { | ||||
| 1620 | error("At most one memory is supported"); | ||||
| 1621 | return false; | ||||
| 1622 | } else { | ||||
| 1623 | module->has_memory = true; | ||||
| 1624 | return true; | ||||
| 1625 | } | ||||
| 1626 | } | ||||
| 1627 | |||||
| 1628 | // Calculate individual global offsets and total size of globals table. This | ||||
| 1629 | // function should be called after all globals have been defined, which is | ||||
| 1630 | // after the import section and the global section, but before the global | ||||
| 1631 | // offsets are accessed, e.g. by the function compilers. The moment when this | ||||
| 1632 | // function should be called is not well-defined, as the global section may | ||||
| 1633 | // not exist. Therefore this function is called multiple times. | ||||
| 1634 | void CalculateGlobalOffsets(WasmModule* module) { | ||||
| 1635 | if (module->globals.empty() || module->untagged_globals_buffer_size != 0 || | ||||
| 1636 | module->tagged_globals_buffer_size != 0) { | ||||
| 1637 | // This function has already been executed before, so we don't have to | ||||
| 1638 | // execute it again. | ||||
| 1639 | return; | ||||
| 1640 | } | ||||
| 1641 | uint32_t untagged_offset = 0; | ||||
| 1642 | uint32_t tagged_offset = 0; | ||||
| 1643 | uint32_t num_imported_mutable_globals = 0; | ||||
| 1644 | for (WasmGlobal& global : module->globals) { | ||||
| 1645 | if (global.mutability && global.imported) { | ||||
| 1646 | global.index = num_imported_mutable_globals++; | ||||
| 1647 | } else if (global.type.is_reference()) { | ||||
| 1648 | global.offset = tagged_offset; | ||||
| 1649 | // All entries in the tagged_globals_buffer have size 1. | ||||
| 1650 | tagged_offset++; | ||||
| 1651 | } else { | ||||
| 1652 | int size = global.type.value_kind_size(); | ||||
| 1653 | untagged_offset = (untagged_offset + size - 1) & ~(size - 1); // align | ||||
| 1654 | global.offset = untagged_offset; | ||||
| 1655 | untagged_offset += size; | ||||
| 1656 | } | ||||
| 1657 | } | ||||
| 1658 | module->untagged_globals_buffer_size = untagged_offset; | ||||
| 1659 | module->tagged_globals_buffer_size = tagged_offset; | ||||
| 1660 | } | ||||
| 1661 | |||||
| 1662 | // Verifies the body (code) of a given function. | ||||
| 1663 | void VerifyFunctionBody(AccountingAllocator* allocator, uint32_t func_num, | ||||
| 1664 | const ModuleWireBytes& wire_bytes, | ||||
| 1665 | const WasmModule* module, WasmFunction* function) { | ||||
| 1666 | WasmFunctionName func_name(function, | ||||
| 1667 | wire_bytes.GetNameOrNull(function, module)); | ||||
| 1668 | if (FLAG_trace_wasm_decoder) { | ||||
| 1669 | StdoutStream{} << "Verifying wasm function " << func_name << std::endl; | ||||
| 1670 | } | ||||
| 1671 | FunctionBody body = { | ||||
| 1672 | function->sig, function->code.offset(), | ||||
| 1673 | start_ + GetBufferRelativeOffset(function->code.offset()), | ||||
| 1674 | start_ + GetBufferRelativeOffset(function->code.end_offset())}; | ||||
| 1675 | |||||
| 1676 | WasmFeatures unused_detected_features = WasmFeatures::None(); | ||||
| 1677 | DecodeResult result = VerifyWasmCode(allocator, enabled_features_, module, | ||||
| 1678 | &unused_detected_features, body); | ||||
| 1679 | |||||
| 1680 | // If the decode failed and this is the first error, set error code and | ||||
| 1681 | // location. | ||||
| 1682 | if (result.failed() && intermediate_error_.empty()) { | ||||
| 1683 | // Wrap the error message from the function decoder. | ||||
| 1684 | std::ostringstream error_msg; | ||||
| 1685 | error_msg << "in function " << func_name << ": " | ||||
| 1686 | << result.error().message(); | ||||
| 1687 | intermediate_error_ = WasmError{result.error().offset(), error_msg.str()}; | ||||
| 1688 | } | ||||
| 1689 | } | ||||
| 1690 | |||||
| 1691 | uint32_t consume_sig_index(WasmModule* module, const FunctionSig** sig) { | ||||
| 1692 | const byte* pos = pc_; | ||||
| 1693 | uint32_t sig_index = consume_u32v("signature index"); | ||||
| 1694 | if (!module->has_signature(sig_index)) { | ||||
| 1695 | errorf(pos, "signature index %u out of bounds (%d signatures)", sig_index, | ||||
| 1696 | static_cast<int>(module->types.size())); | ||||
| 1697 | *sig = nullptr; | ||||
| 1698 | return 0; | ||||
| 1699 | } | ||||
| 1700 | *sig = module->signature(sig_index); | ||||
| 1701 | return sig_index; | ||||
| 1702 | } | ||||
| 1703 | |||||
| 1704 | uint32_t consume_tag_sig_index(WasmModule* module, const FunctionSig** sig) { | ||||
| 1705 | const byte* pos = pc_; | ||||
| 1706 | uint32_t sig_index = consume_sig_index(module, sig); | ||||
| 1707 | if (*sig && (*sig)->return_count() != 0) { | ||||
| 1708 | errorf(pos, "tag signature %u has non-void return", sig_index); | ||||
| 1709 | *sig = nullptr; | ||||
| 1710 | return 0; | ||||
| 1711 | } | ||||
| 1712 | return sig_index; | ||||
| 1713 | } | ||||
| 1714 | |||||
| 1715 | uint32_t consume_count(const char* name, size_t maximum) { | ||||
| 1716 | const byte* p = pc_; | ||||
| 1717 | uint32_t count = consume_u32v(name); | ||||
| 1718 | if (count > maximum) { | ||||
| 1719 | errorf(p, "%s of %u exceeds internal limit of %zu", name, count, maximum); | ||||
| 1720 | return static_cast<uint32_t>(maximum); | ||||
| 1721 | } | ||||
| 1722 | return count; | ||||
| 1723 | } | ||||
| 1724 | |||||
| 1725 | uint32_t consume_func_index(WasmModule* module, WasmFunction** func, | ||||
| 1726 | const char* name) { | ||||
| 1727 | return consume_index(name, &module->functions, func); | ||||
| 1728 | } | ||||
| 1729 | |||||
| 1730 | uint32_t consume_global_index(WasmModule* module, WasmGlobal** global) { | ||||
| 1731 | return consume_index("global index", &module->globals, global); | ||||
| 1732 | } | ||||
| 1733 | |||||
| 1734 | uint32_t consume_table_index(WasmModule* module, WasmTable** table) { | ||||
| 1735 | return consume_index("table index", &module->tables, table); | ||||
| 1736 | } | ||||
| 1737 | |||||
| 1738 | uint32_t consume_tag_index(WasmModule* module, WasmTag** tag) { | ||||
| 1739 | return consume_index("tag index", &module->tags, tag); | ||||
| 1740 | } | ||||
| 1741 | |||||
| 1742 | template <typename T> | ||||
| 1743 | uint32_t consume_index(const char* name, std::vector<T>* vector, T** ptr) { | ||||
| 1744 | const byte* pos = pc_; | ||||
| 1745 | uint32_t index = consume_u32v(name); | ||||
| 1746 | if (index >= vector->size()) { | ||||
| 1747 | errorf(pos, "%s %u out of bounds (%d entr%s)", name, index, | ||||
| 1748 | static_cast<int>(vector->size()), | ||||
| 1749 | vector->size() == 1 ? "y" : "ies"); | ||||
| 1750 | *ptr = nullptr; | ||||
| 1751 | return 0; | ||||
| 1752 | } | ||||
| 1753 | *ptr = &(*vector)[index]; | ||||
| 1754 | return index; | ||||
| 1755 | } | ||||
| 1756 | |||||
| 1757 | uint8_t validate_table_flags(const char* name) { | ||||
| 1758 | uint8_t flags = consume_u8("table limits flags"); | ||||
| 1759 | STATIC_ASSERT(kNoMaximum < kWithMaximum)static_assert(kNoMaximum < kWithMaximum, "kNoMaximum < kWithMaximum" ); | ||||
| 1760 | if (V8_UNLIKELY(flags > kWithMaximum)(__builtin_expect(!!(flags > kWithMaximum), 0))) { | ||||
| 1761 | errorf(pc() - 1, "invalid %s limits flags", name); | ||||
| 1762 | } | ||||
| 1763 | return flags; | ||||
| 1764 | } | ||||
| 1765 | |||||
| 1766 | uint8_t validate_memory_flags(bool* has_shared_memory, bool* is_memory64) { | ||||
| 1767 | uint8_t flags = consume_u8("memory limits flags"); | ||||
| 1768 | *has_shared_memory = false; | ||||
| 1769 | switch (flags) { | ||||
| 1770 | case kNoMaximum: | ||||
| 1771 | case kWithMaximum: | ||||
| 1772 | break; | ||||
| 1773 | case kSharedNoMaximum: | ||||
| 1774 | case kSharedWithMaximum: | ||||
| 1775 | if (!enabled_features_.has_threads()) { | ||||
| 1776 | errorf(pc() - 1, | ||||
| 1777 | "invalid memory limits flags 0x%x (enable via " | ||||
| 1778 | "--experimental-wasm-threads)", | ||||
| 1779 | flags); | ||||
| 1780 | } | ||||
| 1781 | *has_shared_memory = true; | ||||
| 1782 | // V8 does not support shared memory without a maximum. | ||||
| 1783 | if (flags == kSharedNoMaximum) { | ||||
| 1784 | errorf(pc() - 1, | ||||
| 1785 | "memory limits flags must have maximum defined if shared is " | ||||
| 1786 | "true"); | ||||
| 1787 | } | ||||
| 1788 | break; | ||||
| 1789 | case kMemory64NoMaximum: | ||||
| 1790 | case kMemory64WithMaximum: | ||||
| 1791 | if (!enabled_features_.has_memory64()) { | ||||
| 1792 | errorf(pc() - 1, | ||||
| 1793 | "invalid memory limits flags 0x%x (enable via " | ||||
| 1794 | "--experimental-wasm-memory64)", | ||||
| 1795 | flags); | ||||
| 1796 | } | ||||
| 1797 | *is_memory64 = true; | ||||
| 1798 | break; | ||||
| 1799 | default: | ||||
| 1800 | errorf(pc() - 1, "invalid memory limits flags 0x%x", flags); | ||||
| 1801 | break; | ||||
| 1802 | } | ||||
| 1803 | return flags; | ||||
| 1804 | } | ||||
| 1805 | |||||
| 1806 | void consume_resizable_limits(const char* name, const char* units, | ||||
| 1807 | uint32_t max_initial, uint32_t* initial, | ||||
| 1808 | bool* has_max, uint32_t max_maximum, | ||||
| 1809 | uint32_t* maximum, uint8_t flags) { | ||||
| 1810 | const byte* pos = pc(); | ||||
| 1811 | // For memory64 we need to read the numbers as LEB-encoded 64-bit unsigned | ||||
| 1812 | // integer. All V8 limits are still within uint32_t range though. | ||||
| 1813 | const bool is_memory64 = | ||||
| 1814 | flags == kMemory64NoMaximum || flags == kMemory64WithMaximum; | ||||
| 1815 | uint64_t initial_64 = is_memory64 ? consume_u64v("initial size") | ||||
| 1816 | : consume_u32v("initial size"); | ||||
| 1817 | if (initial_64 > max_initial) { | ||||
| 1818 | errorf(pos, | ||||
| 1819 | "initial %s size (%" PRIu64"l" "u" | ||||
| 1820 | " %s) is larger than implementation limit (%u)", | ||||
| 1821 | name, initial_64, units, max_initial); | ||||
| 1822 | } | ||||
| 1823 | *initial = static_cast<uint32_t>(initial_64); | ||||
| 1824 | if (flags & 1) { | ||||
| 1825 | *has_max = true; | ||||
| 1826 | pos = pc(); | ||||
| 1827 | uint64_t maximum_64 = is_memory64 ? consume_u64v("maximum size") | ||||
| 1828 | : consume_u32v("maximum size"); | ||||
| 1829 | if (maximum_64 > max_maximum) { | ||||
| 1830 | errorf(pos, | ||||
| 1831 | "maximum %s size (%" PRIu64"l" "u" | ||||
| 1832 | " %s) is larger than implementation limit (%u)", | ||||
| 1833 | name, maximum_64, units, max_maximum); | ||||
| 1834 | } | ||||
| 1835 | if (maximum_64 < *initial) { | ||||
| 1836 | errorf(pos, | ||||
| 1837 | "maximum %s size (%" PRIu64"l" "u" " %s) is less than initial (%u %s)", | ||||
| 1838 | name, maximum_64, units, *initial, units); | ||||
| 1839 | } | ||||
| 1840 | *maximum = static_cast<uint32_t>(maximum_64); | ||||
| 1841 | } else { | ||||
| 1842 | *has_max = false; | ||||
| 1843 | *maximum = max_initial; | ||||
| 1844 | } | ||||
| 1845 | } | ||||
| 1846 | |||||
| 1847 | // Consumes a byte, and emits an error if it does not equal {expected}. | ||||
| 1848 | bool expect_u8(const char* name, uint8_t expected) { | ||||
| 1849 | const byte* pos = pc(); | ||||
| 1850 | uint8_t value = consume_u8(name); | ||||
| 1851 | if (value != expected) { | ||||
| 1852 | errorf(pos, "expected %s 0x%02x, got 0x%02x", name, expected, value); | ||||
| 1853 | return false; | ||||
| 1854 | } | ||||
| 1855 | return true; | ||||
| 1856 | } | ||||
| 1857 | |||||
| 1858 | ConstantExpression consume_init_expr(WasmModule* module, ValueType expected) { | ||||
| 1859 | uint32_t length; | ||||
| 1860 | |||||
| 1861 | // The error message mimics the one generated by the {WasmFullDecoder}. | ||||
| 1862 | #define TYPE_CHECK(found) \ | ||||
| 1863 | if (V8_UNLIKELY(!IsSubtypeOf(found, expected, module_.get()))(__builtin_expect(!!(!IsSubtypeOf(found, expected, module_.get ())), 0))) { \ | ||||
| 1864 | errorf(pc() + 1, \ | ||||
| 1865 | "type error in init. expression[0] (expected %s, got %s)", \ | ||||
| 1866 | expected.name().c_str(), found.name().c_str()); \ | ||||
| 1867 | return {}; \ | ||||
| 1868 | } | ||||
| 1869 | |||||
| 1870 | // To avoid initializing a {WasmFullDecoder} for the most common | ||||
| 1871 | // expressions, we replicate their decoding and validation here. The | ||||
| 1872 | // manually handled cases correspond to {ConstantExpression}'s kinds. | ||||
| 1873 | // We need to make sure to check that the expression ends in {kExprEnd}; | ||||
| 1874 | // otherwise, it is just the first operand of a composite expression, and we | ||||
| 1875 | // fall back to the default case. | ||||
| 1876 | if (!more()) { | ||||
| 1877 | error("Beyond end of code"); | ||||
| 1878 | return {}; | ||||
| 1879 | } | ||||
| 1880 | switch (static_cast<WasmOpcode>(*pc())) { | ||||
| 1881 | case kExprI32Const: { | ||||
| 1882 | int32_t value = | ||||
| 1883 | read_i32v<kFullValidation>(pc() + 1, &length, "i32.const"); | ||||
| 1884 | if (V8_UNLIKELY(failed())(__builtin_expect(!!(failed()), 0))) return {}; | ||||
| 1885 | if (V8_LIKELY(lookahead(1 + length, kExprEnd))(__builtin_expect(!!(lookahead(1 + length, kExprEnd)), 1))) { | ||||
| 1886 | TYPE_CHECK(kWasmI32) | ||||
| 1887 | consume_bytes(length + 2); | ||||
| 1888 | return ConstantExpression::I32Const(value); | ||||
| 1889 | } | ||||
| 1890 | break; | ||||
| 1891 | } | ||||
| 1892 | case kExprRefFunc: { | ||||
| 1893 | uint32_t index = | ||||
| 1894 | read_u32v<kFullValidation>(pc() + 1, &length, "ref.func"); | ||||
| 1895 | if (V8_UNLIKELY(failed())(__builtin_expect(!!(failed()), 0))) return {}; | ||||
| 1896 | if (V8_LIKELY(lookahead(1 + length, kExprEnd))(__builtin_expect(!!(lookahead(1 + length, kExprEnd)), 1))) { | ||||
| 1897 | if (V8_UNLIKELY(index >= module_->functions.size())(__builtin_expect(!!(index >= module_->functions.size() ), 0))) { | ||||
| 1898 | errorf(pc() + 1, "function index %u out of bounds", index); | ||||
| 1899 | return {}; | ||||
| 1900 | } | ||||
| 1901 | ValueType type = | ||||
| 1902 | enabled_features_.has_typed_funcref() | ||||
| 1903 | ? ValueType::Ref(module_->functions[index].sig_index, | ||||
| 1904 | kNonNullable) | ||||
| 1905 | : kWasmFuncRef; | ||||
| 1906 | TYPE_CHECK(type) | ||||
| 1907 | module_->functions[index].declared = true; | ||||
| 1908 | consume_bytes(length + 2); | ||||
| 1909 | return ConstantExpression::RefFunc(index); | ||||
| 1910 | } | ||||
| 1911 | break; | ||||
| 1912 | } | ||||
| 1913 | case kExprRefNull: { | ||||
| 1914 | HeapType type = value_type_reader::read_heap_type<kFullValidation>( | ||||
| 1915 | this, pc() + 1, &length, module_.get(), enabled_features_); | ||||
| 1916 | if (V8_UNLIKELY(failed())(__builtin_expect(!!(failed()), 0))) return {}; | ||||
| 1917 | if (V8_LIKELY(lookahead(1 + length, kExprEnd))(__builtin_expect(!!(lookahead(1 + length, kExprEnd)), 1))) { | ||||
| 1918 | TYPE_CHECK(ValueType::Ref(type, kNullable)) | ||||
| 1919 | consume_bytes(length + 2); | ||||
| 1920 | return ConstantExpression::RefNull(type.representation()); | ||||
| 1921 | } | ||||
| 1922 | break; | ||||
| 1923 | } | ||||
| 1924 | default: | ||||
| 1925 | break; | ||||
| 1926 | } | ||||
| 1927 | #undef TYPE_CHECK | ||||
| 1928 | |||||
| 1929 | auto sig = FixedSizeSignature<ValueType>::Returns(expected); | ||||
| 1930 | FunctionBody body(&sig, buffer_offset_, pc_, end_); | ||||
| 1931 | WasmFeatures detected; | ||||
| 1932 | WasmFullDecoder<Decoder::kFullValidation, InitExprInterface, | ||||
| 1933 | kInitExpression> | ||||
| 1934 | decoder(&init_expr_zone_, module, enabled_features_, &detected, body, | ||||
| 1935 | module); | ||||
| 1936 | |||||
| 1937 | uint32_t offset = this->pc_offset(); | ||||
| 1938 | |||||
| 1939 | decoder.DecodeFunctionBody(); | ||||
| 1940 | |||||
| 1941 | this->pc_ = decoder.end(); | ||||
| 1942 | |||||
| 1943 | if (decoder.failed()) { | ||||
| 1944 | error(decoder.error().offset(), decoder.error().message().c_str()); | ||||
| 1945 | return {}; | ||||
| 1946 | } | ||||
| 1947 | |||||
| 1948 | if (!decoder.interface().end_found()) { | ||||
| 1949 | error("Initializer expression is missing 'end'"); | ||||
| 1950 | return {}; | ||||
| 1951 | } | ||||
| 1952 | |||||
| 1953 | return ConstantExpression::WireBytes( | ||||
| 1954 | offset, static_cast<uint32_t>(decoder.end() - decoder.start())); | ||||
| 1955 | } | ||||
| 1956 | |||||
| 1957 | // Read a mutability flag | ||||
| 1958 | bool consume_mutability() { | ||||
| 1959 | byte val = consume_u8("mutability"); | ||||
| 1960 | if (val > 1) error(pc_ - 1, "invalid mutability"); | ||||
| 1961 | return val != 0; | ||||
| 1962 | } | ||||
| 1963 | |||||
| 1964 | ValueType consume_value_type() { | ||||
| 1965 | uint32_t type_length; | ||||
| 1966 | ValueType result = value_type_reader::read_value_type<kFullValidation>( | ||||
| 1967 | this, this->pc(), &type_length, module_.get(), | ||||
| 1968 | origin_ == kWasmOrigin ? enabled_features_ : WasmFeatures::None()); | ||||
| 1969 | consume_bytes(type_length, "value type"); | ||||
| 1970 | return result; | ||||
| 1971 | } | ||||
| 1972 | |||||
| 1973 | HeapType consume_super_type() { | ||||
| 1974 | return value_type_reader::consume_heap_type(this, module_.get(), | ||||
| 1975 | enabled_features_); | ||||
| 1976 | } | ||||
| 1977 | |||||
| 1978 | ValueType consume_storage_type() { | ||||
| 1979 | uint8_t opcode = read_u8<kFullValidation>(this->pc()); | ||||
| 1980 | switch (opcode) { | ||||
| 1981 | case kI8Code: | ||||
| 1982 | consume_bytes(1, "i8"); | ||||
| 1983 | return kWasmI8; | ||||
| 1984 | case kI16Code: | ||||
| 1985 | consume_bytes(1, "i16"); | ||||
| 1986 | return kWasmI16; | ||||
| 1987 | default: | ||||
| 1988 | // It is not a packed type, so it has to be a value type. | ||||
| 1989 | return consume_value_type(); | ||||
| 1990 | } | ||||
| 1991 | } | ||||
| 1992 | |||||
| 1993 | // Reads a reference type for tables and element segment headers. | ||||
| 1994 | ValueType consume_reference_type() { | ||||
| 1995 | const byte* position = pc(); | ||||
| 1996 | ValueType result = consume_value_type(); | ||||
| 1997 | if (!result.is_reference()) { | ||||
| 1998 | error(position, "expected reference type"); | ||||
| 1999 | } | ||||
| 2000 | return result; | ||||
| 2001 | } | ||||
| 2002 | |||||
| 2003 | const FunctionSig* consume_sig(Zone* zone) { | ||||
| 2004 | // Parse parameter types. | ||||
| 2005 | uint32_t param_count = | ||||
| 2006 | consume_count("param count", kV8MaxWasmFunctionParams); | ||||
| 2007 | if (failed()) return nullptr; | ||||
| 2008 | std::vector<ValueType> params; | ||||
| 2009 | for (uint32_t i = 0; ok() && i < param_count; ++i) { | ||||
| 2010 | params.push_back(consume_value_type()); | ||||
| 2011 | } | ||||
| 2012 | std::vector<ValueType> returns; | ||||
| 2013 | |||||
| 2014 | // Parse return types. | ||||
| 2015 | uint32_t return_count = | ||||
| 2016 | consume_count("return count", kV8MaxWasmFunctionReturns); | ||||
| 2017 | if (failed()) return nullptr; | ||||
| 2018 | for (uint32_t i = 0; ok() && i < return_count; ++i) { | ||||
| 2019 | returns.push_back(consume_value_type()); | ||||
| 2020 | } | ||||
| 2021 | if (failed()) return nullptr; | ||||
| 2022 | |||||
| 2023 | // FunctionSig stores the return types first. | ||||
| 2024 | ValueType* buffer = zone->NewArray<ValueType>(param_count + return_count); | ||||
| 2025 | uint32_t b = 0; | ||||
| 2026 | for (uint32_t i = 0; i < return_count; ++i) buffer[b++] = returns[i]; | ||||
| 2027 | for (uint32_t i = 0; i < param_count; ++i) buffer[b++] = params[i]; | ||||
| 2028 | |||||
| 2029 | return zone->New<FunctionSig>(return_count, param_count, buffer); | ||||
| 2030 | } | ||||
| 2031 | |||||
| 2032 | const StructType* consume_struct(Zone* zone) { | ||||
| 2033 | uint32_t field_count = consume_count("field count", kV8MaxWasmStructFields); | ||||
| 2034 | if (failed()) return nullptr; | ||||
| 2035 | ValueType* fields = zone->NewArray<ValueType>(field_count); | ||||
| 2036 | bool* mutabilities = zone->NewArray<bool>(field_count); | ||||
| 2037 | for (uint32_t i = 0; ok() && i < field_count; ++i) { | ||||
| 2038 | fields[i] = consume_storage_type(); | ||||
| 2039 | mutabilities[i] = consume_mutability(); | ||||
| 2040 | } | ||||
| 2041 | if (failed()) return nullptr; | ||||
| 2042 | uint32_t* offsets = zone->NewArray<uint32_t>(field_count); | ||||
| 2043 | return zone->New<StructType>(field_count, offsets, fields, mutabilities); | ||||
| 2044 | } | ||||
| 2045 | |||||
| 2046 | const ArrayType* consume_array(Zone* zone) { | ||||
| 2047 | ValueType element_type = consume_storage_type(); | ||||
| 2048 | bool mutability = consume_mutability(); | ||||
| 2049 | if (failed()) return nullptr; | ||||
| 2050 | return zone->New<ArrayType>(element_type, mutability); | ||||
| 2051 | } | ||||
| 2052 | |||||
| 2053 | // Consume the attribute field of an exception. | ||||
| 2054 | uint32_t consume_exception_attribute() { | ||||
| 2055 | const byte* pos = pc_; | ||||
| 2056 | uint32_t attribute = consume_u32v("exception attribute"); | ||||
| 2057 | if (attribute != kExceptionAttribute) { | ||||
| 2058 | errorf(pos, "exception attribute %u not supported", attribute); | ||||
| 2059 | return 0; | ||||
| 2060 | } | ||||
| 2061 | return attribute; | ||||
| 2062 | } | ||||
| 2063 | |||||
| 2064 | WasmElemSegment consume_element_segment_header() { | ||||
| 2065 | const byte* pos = pc(); | ||||
| 2066 | |||||
| 2067 | // The mask for the bit in the flag which indicates if the segment is | ||||
| 2068 | // active or not (0 is active). | ||||
| 2069 | constexpr uint8_t kNonActiveMask = 1 << 0; | ||||
| 2070 | // The mask for the bit in the flag which indicates: | ||||
| 2071 | // - for active tables, if the segment has an explicit table index field. | ||||
| 2072 | // - for non-active tables, whether the table is declarative (vs. passive). | ||||
| 2073 | constexpr uint8_t kHasTableIndexOrIsDeclarativeMask = 1 << 1; | ||||
| 2074 | // The mask for the bit in the flag which indicates if the functions of this | ||||
| 2075 | // segment are defined as function indices (0) or init. expressions (1). | ||||
| 2076 | constexpr uint8_t kExpressionsAsElementsMask = 1 << 2; | ||||
| 2077 | constexpr uint8_t kFullMask = kNonActiveMask | | ||||
| 2078 | kHasTableIndexOrIsDeclarativeMask | | ||||
| 2079 | kExpressionsAsElementsMask; | ||||
| 2080 | |||||
| 2081 | uint32_t flag = consume_u32v("flag"); | ||||
| 2082 | if ((flag & kFullMask) != flag) { | ||||
| 2083 | errorf(pos, "illegal flag value %u. Must be between 0 and 7", flag); | ||||
| 2084 | return {}; | ||||
| 2085 | } | ||||
| 2086 | |||||
| 2087 | const WasmElemSegment::Status status = | ||||
| 2088 | (flag & kNonActiveMask) ? (flag & kHasTableIndexOrIsDeclarativeMask) | ||||
| 2089 | ? WasmElemSegment::kStatusDeclarative | ||||
| 2090 | : WasmElemSegment::kStatusPassive | ||||
| 2091 | : WasmElemSegment::kStatusActive; | ||||
| 2092 | const bool is_active = status == WasmElemSegment::kStatusActive; | ||||
| 2093 | |||||
| 2094 | WasmElemSegment::ElementType element_type = | ||||
| 2095 | flag & kExpressionsAsElementsMask | ||||
| 2096 | ? WasmElemSegment::kExpressionElements | ||||
| 2097 | : WasmElemSegment::kFunctionIndexElements; | ||||
| 2098 | |||||
| 2099 | const bool has_table_index = | ||||
| 2100 | is_active && (flag & kHasTableIndexOrIsDeclarativeMask); | ||||
| 2101 | uint32_t table_index = has_table_index ? consume_u32v("table index") : 0; | ||||
| 2102 | if (is_active && table_index >= module_->tables.size()) { | ||||
| 2103 | errorf(pos, "out of bounds%s table index %u", | ||||
| 2104 | has_table_index ? " implicit" : "", table_index); | ||||
| 2105 | return {}; | ||||
| 2106 | } | ||||
| 2107 | ValueType table_type = | ||||
| 2108 | is_active ? module_->tables[table_index].type : kWasmBottom; | ||||
| 2109 | |||||
| 2110 | ConstantExpression offset; | ||||
| 2111 | if (is_active) { | ||||
| 2112 | offset = consume_init_expr(module_.get(), kWasmI32); | ||||
| 2113 | // Failed to parse offset initializer, return early. | ||||
| 2114 | if (failed()) return {}; | ||||
| 2115 | } | ||||
| 2116 | |||||
| 2117 | // Denotes an active segment without table index, type, or element kind. | ||||
| 2118 | const bool backwards_compatible_mode = | ||||
| 2119 | is_active && !(flag & kHasTableIndexOrIsDeclarativeMask); | ||||
| 2120 | ValueType type; | ||||
| 2121 | if (element_type == WasmElemSegment::kExpressionElements) { | ||||
| 2122 | type = | ||||
| 2123 | backwards_compatible_mode ? kWasmFuncRef : consume_reference_type(); | ||||
| 2124 | if (is_active && !IsSubtypeOf(type, table_type, this->module_.get())) { | ||||
| 2125 | errorf(pos, | ||||
| 2126 | "Element segment of type %s is not a subtype of referenced " | ||||
| 2127 | "table %u (of type %s)", | ||||
| 2128 | type.name().c_str(), table_index, table_type.name().c_str()); | ||||
| 2129 | return {}; | ||||
| 2130 | } | ||||
| 2131 | } else { | ||||
| 2132 | if (!backwards_compatible_mode) { | ||||
| 2133 | // We have to check that there is an element kind of type Function. All | ||||
| 2134 | // other element kinds are not valid yet. | ||||
| 2135 | uint8_t val = consume_u8("element kind"); | ||||
| 2136 | if (static_cast<ImportExportKindCode>(val) != kExternalFunction) { | ||||
| 2137 | errorf(pos, "illegal element kind 0x%x. Must be 0x%x", val, | ||||
| 2138 | kExternalFunction); | ||||
| 2139 | return {}; | ||||
| 2140 | } | ||||
| 2141 | } | ||||
| 2142 | if (!is_active) { | ||||
| 2143 | // Declarative and passive segments without explicit type are funcref. | ||||
| 2144 | type = kWasmFuncRef; | ||||
| 2145 | } else { | ||||
| 2146 | type = table_type; | ||||
| 2147 | // Active segments with function indices must reference a function | ||||
| 2148 | // table. TODO(7748): Add support for anyref tables when we have them. | ||||
| 2149 | if (!IsSubtypeOf(table_type, kWasmFuncRef, this->module_.get())) { | ||||
| 2150 | errorf(pos, | ||||
| 2151 | "An active element segment with function indices as elements " | ||||
| 2152 | "must reference a table of %s. Instead, table %u of type %s " | ||||
| 2153 | "is referenced.", | ||||
| 2154 | enabled_features_.has_typed_funcref() | ||||
| 2155 | ? "a subtype of type funcref" | ||||
| 2156 | : "type funcref", | ||||
| 2157 | table_index, table_type.name().c_str()); | ||||
| 2158 | return {}; | ||||
| 2159 | } | ||||
| 2160 | } | ||||
| 2161 | } | ||||
| 2162 | |||||
| 2163 | if (is_active) { | ||||
| 2164 | return {type, table_index, std::move(offset), element_type}; | ||||
| 2165 | } else { | ||||
| 2166 | return {type, status, element_type}; | ||||
| 2167 | } | ||||
| 2168 | } | ||||
| 2169 | |||||
| 2170 | void consume_data_segment_header(bool* is_active, uint32_t* index, | ||||
| 2171 | ConstantExpression* offset) { | ||||
| 2172 | const byte* pos = pc(); | ||||
| 2173 | uint32_t flag = consume_u32v("flag"); | ||||
| 2174 | |||||
| 2175 | // Some flag values are only valid for specific proposals. | ||||
| 2176 | if (flag != SegmentFlags::kActiveNoIndex && | ||||
| 2177 | flag != SegmentFlags::kPassive && | ||||
| 2178 | flag != SegmentFlags::kActiveWithIndex) { | ||||
| 2179 | errorf(pos, "illegal flag value %u. Must be 0, 1, or 2", flag); | ||||
| 2180 | return; | ||||
| 2181 | } | ||||
| 2182 | |||||
| 2183 | // We know now that the flag is valid. Time to read the rest. | ||||
| 2184 | ValueType expected_type = module_->is_memory64 ? kWasmI64 : kWasmI32; | ||||
| 2185 | if (flag == SegmentFlags::kActiveNoIndex) { | ||||
| 2186 | *is_active = true; | ||||
| 2187 | *index = 0; | ||||
| 2188 | *offset = consume_init_expr(module_.get(), expected_type); | ||||
| 2189 | return; | ||||
| 2190 | } | ||||
| 2191 | if (flag == SegmentFlags::kPassive) { | ||||
| 2192 | *is_active = false; | ||||
| 2193 | return; | ||||
| 2194 | } | ||||
| 2195 | if (flag == SegmentFlags::kActiveWithIndex) { | ||||
| 2196 | *is_active = true; | ||||
| 2197 | *index = consume_u32v("memory index"); | ||||
| 2198 | *offset = consume_init_expr(module_.get(), expected_type); | ||||
| 2199 | } | ||||
| 2200 | } | ||||
| 2201 | |||||
| 2202 | uint32_t consume_element_func_index(ValueType expected) { | ||||
| 2203 | WasmFunction* func = nullptr; | ||||
| 2204 | const byte* initial_pc = pc(); | ||||
| 2205 | uint32_t index = | ||||
| 2206 | consume_func_index(module_.get(), &func, "element function index"); | ||||
| 2207 | if (failed()) return index; | ||||
| 2208 | DCHECK_NOT_NULL(func)((void) 0); | ||||
| 2209 | DCHECK_EQ(index, func->func_index)((void) 0); | ||||
| 2210 | ValueType entry_type = ValueType::Ref(func->sig_index, kNonNullable); | ||||
| 2211 | if (V8_UNLIKELY(!IsSubtypeOf(entry_type, expected, module_.get()))(__builtin_expect(!!(!IsSubtypeOf(entry_type, expected, module_ .get())), 0))) { | ||||
| 2212 | errorf(initial_pc, | ||||
| 2213 | "Invalid type in element entry: expected %s, got %s instead.", | ||||
| 2214 | expected.name().c_str(), entry_type.name().c_str()); | ||||
| 2215 | return index; | ||||
| 2216 | } | ||||
| 2217 | func->declared = true; | ||||
| 2218 | return index; | ||||
| 2219 | } | ||||
| 2220 | }; | ||||
| 2221 | |||||
| 2222 | ModuleResult DecodeWasmModule( | ||||
| 2223 | const WasmFeatures& enabled, const byte* module_start, | ||||
| 2224 | const byte* module_end, bool verify_functions, ModuleOrigin origin, | ||||
| 2225 | Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder, | ||||
| 2226 | v8::metrics::Recorder::ContextId context_id, DecodingMethod decoding_method, | ||||
| 2227 | AccountingAllocator* allocator) { | ||||
| 2228 | size_t size = module_end - module_start; | ||||
| 2229 | CHECK_LE(module_start, module_end)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base ::pass_value_or_ref<decltype(module_start)>::type, typename ::v8::base::pass_value_or_ref<decltype(module_end)>::type >((module_start), (module_end)); do { if ((__builtin_expect (!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "module_start" " " "<=" " " "module_end"); } } while (false); } while (false ); | ||||
| 2230 | size_t max_size = max_module_size(); | ||||
| 2231 | if (size > max_size) { | ||||
| 2232 | return ModuleResult{ | ||||
| 2233 | WasmError{0, "size > maximum module size (%zu): %zu", max_size, size}}; | ||||
| 2234 | } | ||||
| 2235 | // TODO(bradnelson): Improve histogram handling of size_t. | ||||
| 2236 | auto size_counter = | ||||
| 2237 | SELECT_WASM_COUNTER(counters, origin, wasm, module_size_bytes)((origin) == kWasmOrigin ? (counters)->wasm_wasm_module_size_bytes () : (counters)->wasm_asm_module_size_bytes()); | ||||
| 2238 | size_counter->AddSample(static_cast<int>(size)); | ||||
| 2239 | // Signatures are stored in zone memory, which have the same lifetime | ||||
| 2240 | // as the {module}. | ||||
| 2241 | ModuleDecoderImpl decoder(enabled, module_start, module_end, origin); | ||||
| 2242 | v8::metrics::WasmModuleDecoded metrics_event; | ||||
| 2243 | base::ElapsedTimer timer; | ||||
| 2244 | timer.Start(); | ||||
| 2245 | base::ThreadTicks thread_ticks = base::ThreadTicks::IsSupported() | ||||
| 2246 | ? base::ThreadTicks::Now() | ||||
| 2247 | : base::ThreadTicks(); | ||||
| 2248 | ModuleResult result = | ||||
| 2249 | decoder.DecodeModule(counters, allocator, verify_functions); | ||||
| 2250 | |||||
| 2251 | // Record event metrics. | ||||
| 2252 | metrics_event.wall_clock_duration_in_us = timer.Elapsed().InMicroseconds(); | ||||
| 2253 | timer.Stop(); | ||||
| 2254 | if (!thread_ticks.IsNull()) { | ||||
| 2255 | metrics_event.cpu_duration_in_us = | ||||
| 2256 | (base::ThreadTicks::Now() - thread_ticks).InMicroseconds(); | ||||
| 2257 | } | ||||
| 2258 | metrics_event.success = decoder.ok() && result.ok(); | ||||
| 2259 | metrics_event.async = decoding_method == DecodingMethod::kAsync || | ||||
| 2260 | decoding_method == DecodingMethod::kAsyncStream; | ||||
| 2261 | metrics_event.streamed = decoding_method == DecodingMethod::kSyncStream || | ||||
| 2262 | decoding_method == DecodingMethod::kAsyncStream; | ||||
| 2263 | if (result.ok()) { | ||||
| 2264 | metrics_event.function_count = result.value()->num_declared_functions; | ||||
| 2265 | } else if (auto&& module = decoder.shared_module()) { | ||||
| 2266 | metrics_event.function_count = module->num_declared_functions; | ||||
| 2267 | } | ||||
| 2268 | metrics_event.module_size_in_bytes = size; | ||||
| 2269 | metrics_recorder->DelayMainThreadEvent(metrics_event, context_id); | ||||
| 2270 | |||||
| 2271 | return result; | ||||
| 2272 | } | ||||
| 2273 | |||||
| 2274 | ModuleDecoder::ModuleDecoder(const WasmFeatures& enabled) | ||||
| 2275 | : enabled_features_(enabled) {} | ||||
| 2276 | |||||
| 2277 | ModuleDecoder::~ModuleDecoder() = default; | ||||
| 2278 | |||||
| 2279 | const std::shared_ptr<WasmModule>& ModuleDecoder::shared_module() const { | ||||
| 2280 | return impl_->shared_module(); | ||||
| 2281 | } | ||||
| 2282 | |||||
| 2283 | void ModuleDecoder::StartDecoding( | ||||
| 2284 | Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder, | ||||
| 2285 | v8::metrics::Recorder::ContextId context_id, AccountingAllocator* allocator, | ||||
| 2286 | ModuleOrigin origin) { | ||||
| 2287 | DCHECK_NULL(impl_)((void) 0); | ||||
| 2288 | impl_.reset(new ModuleDecoderImpl(enabled_features_, origin)); | ||||
| 2289 | impl_->StartDecoding(counters, allocator); | ||||
| 2290 | } | ||||
| 2291 | |||||
| 2292 | void ModuleDecoder::DecodeModuleHeader(base::Vector<const uint8_t> bytes, | ||||
| 2293 | uint32_t offset) { | ||||
| 2294 | impl_->DecodeModuleHeader(bytes, offset); | ||||
| 2295 | } | ||||
| 2296 | |||||
| 2297 | void ModuleDecoder::DecodeSection(SectionCode section_code, | ||||
| 2298 | base::Vector<const uint8_t> bytes, | ||||
| 2299 | uint32_t offset, bool verify_functions) { | ||||
| 2300 | impl_->DecodeSection(section_code, bytes, offset, verify_functions); | ||||
| |||||
| 2301 | } | ||||
| 2302 | |||||
| 2303 | void ModuleDecoder::DecodeFunctionBody(uint32_t index, uint32_t length, | ||||
| 2304 | uint32_t offset, bool verify_functions) { | ||||
| 2305 | impl_->DecodeFunctionBody(index, length, offset, verify_functions); | ||||
| 2306 | } | ||||
| 2307 | |||||
| 2308 | void ModuleDecoder::StartCodeSection() { impl_->StartCodeSection(); } | ||||
| 2309 | |||||
| 2310 | bool ModuleDecoder::CheckFunctionsCount(uint32_t functions_count, | ||||
| 2311 | uint32_t error_offset) { | ||||
| 2312 | return impl_->CheckFunctionsCount(functions_count, error_offset); | ||||
| 2313 | } | ||||
| 2314 | |||||
| 2315 | ModuleResult ModuleDecoder::FinishDecoding(bool verify_functions) { | ||||
| 2316 | return impl_->FinishDecoding(verify_functions); | ||||
| 2317 | } | ||||
| 2318 | |||||
| 2319 | void ModuleDecoder::set_code_section(uint32_t offset, uint32_t size) { | ||||
| 2320 | return impl_->set_code_section(offset, size); | ||||
| 2321 | } | ||||
| 2322 | |||||
| 2323 | size_t ModuleDecoder::IdentifyUnknownSection(ModuleDecoder* decoder, | ||||
| 2324 | base::Vector<const uint8_t> bytes, | ||||
| 2325 | uint32_t offset, | ||||
| 2326 | SectionCode* result) { | ||||
| 2327 | if (!decoder->ok()) return 0; | ||||
| 2328 | decoder->impl_->Reset(bytes, offset); | ||||
| 2329 | *result = IdentifyUnknownSectionInternal(decoder->impl_.get()); | ||||
| 2330 | return decoder->impl_->pc() - bytes.begin(); | ||||
| 2331 | } | ||||
| 2332 | |||||
| 2333 | bool ModuleDecoder::ok() { return impl_->ok(); } | ||||
| 2334 | |||||
| 2335 | const FunctionSig* DecodeWasmSignatureForTesting(const WasmFeatures& enabled, | ||||
| 2336 | Zone* zone, const byte* start, | ||||
| 2337 | const byte* end) { | ||||
| 2338 | ModuleDecoderImpl decoder(enabled, start, end, kWasmOrigin); | ||||
| 2339 | return decoder.DecodeFunctionSignature(zone, start); | ||||
| 2340 | } | ||||
| 2341 | |||||
| 2342 | ConstantExpression DecodeWasmInitExprForTesting(const WasmFeatures& enabled, | ||||
| 2343 | const byte* start, | ||||
| 2344 | const byte* end, | ||||
| 2345 | ValueType expected) { | ||||
| 2346 | ModuleDecoderImpl decoder(enabled, start, end, kWasmOrigin); | ||||
| 2347 | AccountingAllocator allocator; | ||||
| 2348 | decoder.StartDecoding(nullptr, &allocator); | ||||
| 2349 | return decoder.DecodeInitExprForTesting(expected); | ||||
| 2350 | } | ||||
| 2351 | |||||
| 2352 | FunctionResult DecodeWasmFunctionForTesting( | ||||
| 2353 | const WasmFeatures& enabled, Zone* zone, const ModuleWireBytes& wire_bytes, | ||||
| 2354 | const WasmModule* module, const byte* function_start, | ||||
| 2355 | const byte* function_end, Counters* counters) { | ||||
| 2356 | size_t size = function_end - function_start; | ||||
| 2357 | CHECK_LE(function_start, function_end)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base ::pass_value_or_ref<decltype(function_start)>::type, typename ::v8::base::pass_value_or_ref<decltype(function_end)>:: type>((function_start), (function_end)); do { if ((__builtin_expect (!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "function_start" " " "<=" " " "function_end"); } } while (false); } while ( false); | ||||
| 2358 | if (size > kV8MaxWasmFunctionSize) { | ||||
| 2359 | return FunctionResult{WasmError{0, | ||||
| 2360 | "size > maximum function size (%zu): %zu", | ||||
| 2361 | kV8MaxWasmFunctionSize, size}}; | ||||
| 2362 | } | ||||
| 2363 | ModuleDecoderImpl decoder(enabled, function_start, function_end, kWasmOrigin); | ||||
| 2364 | decoder.SetCounters(counters); | ||||
| 2365 | return decoder.DecodeSingleFunction(zone, wire_bytes, module, | ||||
| 2366 | std::make_unique<WasmFunction>()); | ||||
| 2367 | } | ||||
| 2368 | |||||
| 2369 | AsmJsOffsetsResult DecodeAsmJsOffsets( | ||||
| 2370 | base::Vector<const uint8_t> encoded_offsets) { | ||||
| 2371 | std::vector<AsmJsOffsetFunctionEntries> functions; | ||||
| 2372 | |||||
| 2373 | Decoder decoder(encoded_offsets); | ||||
| 2374 | uint32_t functions_count = decoder.consume_u32v("functions count"); | ||||
| 2375 | // Consistency check. | ||||
| 2376 | DCHECK_GE(encoded_offsets.size(), functions_count)((void) 0); | ||||
| 2377 | functions.reserve(functions_count); | ||||
| 2378 | |||||
| 2379 | for (uint32_t i = 0; i < functions_count; ++i) { | ||||
| 2380 | uint32_t size = decoder.consume_u32v("table size"); | ||||
| 2381 | if (size == 0) { | ||||
| 2382 | functions.emplace_back(); | ||||
| 2383 | continue; | ||||
| 2384 | } | ||||
| 2385 | DCHECK(decoder.checkAvailable(size))((void) 0); | ||||
| 2386 | const byte* table_end = decoder.pc() + size; | ||||
| 2387 | uint32_t locals_size = decoder.consume_u32v("locals size"); | ||||
| 2388 | int function_start_position = decoder.consume_u32v("function start pos"); | ||||
| 2389 | int function_end_position = function_start_position; | ||||
| 2390 | int last_byte_offset = locals_size; | ||||
| 2391 | int last_asm_position = function_start_position; | ||||
| 2392 | std::vector<AsmJsOffsetEntry> func_asm_offsets; | ||||
| 2393 | func_asm_offsets.reserve(size / 4); // conservative estimation | ||||
| 2394 | // Add an entry for the stack check, associated with position 0. | ||||
| 2395 | func_asm_offsets.push_back( | ||||
| 2396 | {0, function_start_position, function_start_position}); | ||||
| 2397 | while (decoder.pc() < table_end) { | ||||
| 2398 | DCHECK(decoder.ok())((void) 0); | ||||
| 2399 | last_byte_offset += decoder.consume_u32v("byte offset delta"); | ||||
| 2400 | int call_position = | ||||
| 2401 | last_asm_position + decoder.consume_i32v("call position delta"); | ||||
| 2402 | int to_number_position = | ||||
| 2403 | call_position + decoder.consume_i32v("to_number position delta"); | ||||
| 2404 | last_asm_position = to_number_position; | ||||
| 2405 | if (decoder.pc() == table_end) { | ||||
| 2406 | // The last entry is the function end marker. | ||||
| 2407 | DCHECK_EQ(call_position, to_number_position)((void) 0); | ||||
| 2408 | function_end_position = call_position; | ||||
| 2409 | } else { | ||||
| 2410 | func_asm_offsets.push_back( | ||||
| 2411 | {last_byte_offset, call_position, to_number_position}); | ||||
| 2412 | } | ||||
| 2413 | } | ||||
| 2414 | DCHECK_EQ(decoder.pc(), table_end)((void) 0); | ||||
| 2415 | functions.emplace_back(AsmJsOffsetFunctionEntries{ | ||||
| 2416 | function_start_position, function_end_position, | ||||
| 2417 | std::move(func_asm_offsets)}); | ||||
| 2418 | } | ||||
| 2419 | DCHECK(decoder.ok())((void) 0); | ||||
| 2420 | DCHECK(!decoder.more())((void) 0); | ||||
| 2421 | |||||
| 2422 | return decoder.toResult(AsmJsOffsets{std::move(functions)}); | ||||
| 2423 | } | ||||
| 2424 | |||||
| 2425 | std::vector<CustomSectionOffset> DecodeCustomSections(const byte* start, | ||||
| 2426 | const byte* end) { | ||||
| 2427 | Decoder decoder(start, end); | ||||
| 2428 | decoder.consume_bytes(4, "wasm magic"); | ||||
| 2429 | decoder.consume_bytes(4, "wasm version"); | ||||
| 2430 | |||||
| 2431 | std::vector<CustomSectionOffset> result; | ||||
| 2432 | |||||
| 2433 | while (decoder.more()) { | ||||
| 2434 | byte section_code = decoder.consume_u8("section code"); | ||||
| 2435 | uint32_t section_length = decoder.consume_u32v("section length"); | ||||
| 2436 | uint32_t section_start = decoder.pc_offset(); | ||||
| 2437 | if (section_code != 0) { | ||||
| 2438 | // Skip known sections. | ||||
| 2439 | decoder.consume_bytes(section_length, "section bytes"); | ||||
| 2440 | continue; | ||||
| 2441 | } | ||||
| 2442 | uint32_t name_length = decoder.consume_u32v("name length"); | ||||
| 2443 | uint32_t name_offset = decoder.pc_offset(); | ||||
| 2444 | decoder.consume_bytes(name_length, "section name"); | ||||
| 2445 | uint32_t payload_offset = decoder.pc_offset(); | ||||
| 2446 | if (section_length < (payload_offset - section_start)) { | ||||
| 2447 | decoder.error("invalid section length"); | ||||
| 2448 | break; | ||||
| 2449 | } | ||||
| 2450 | uint32_t payload_length = section_length - (payload_offset - section_start); | ||||
| 2451 | decoder.consume_bytes(payload_length); | ||||
| 2452 | if (decoder.failed()) break; | ||||
| 2453 | result.push_back({{section_start, section_length}, | ||||
| 2454 | {name_offset, name_length}, | ||||
| 2455 | {payload_offset, payload_length}}); | ||||
| 2456 | } | ||||
| 2457 | |||||
| 2458 | return result; | ||||
| 2459 | } | ||||
| 2460 | |||||
| 2461 | namespace { | ||||
| 2462 | |||||
| 2463 | bool FindNameSection(Decoder* decoder) { | ||||
| 2464 | static constexpr int kModuleHeaderSize = 8; | ||||
| 2465 | decoder->consume_bytes(kModuleHeaderSize, "module header"); | ||||
| 2466 | |||||
| 2467 | WasmSectionIterator section_iter(decoder); | ||||
| 2468 | |||||
| 2469 | while (decoder->ok() && section_iter.more() && | ||||
| 2470 | section_iter.section_code() != kNameSectionCode) { | ||||
| 2471 | section_iter.advance(true); | ||||
| 2472 | } | ||||
| 2473 | if (!section_iter.more()) return false; | ||||
| 2474 | |||||
| 2475 | // Reset the decoder to not read beyond the name section end. | ||||
| 2476 | decoder->Reset(section_iter.payload(), decoder->pc_offset()); | ||||
| 2477 | return true; | ||||
| 2478 | } | ||||
| 2479 | |||||
| 2480 | } // namespace | ||||
| 2481 | |||||
| 2482 | void DecodeFunctionNames(const byte* module_start, const byte* module_end, | ||||
| 2483 | std::unordered_map<uint32_t, WireBytesRef>* names) { | ||||
| 2484 | DCHECK_NOT_NULL(names)((void) 0); | ||||
| 2485 | DCHECK(names->empty())((void) 0); | ||||
| 2486 | |||||
| 2487 | Decoder decoder(module_start, module_end); | ||||
| 2488 | if (FindNameSection(&decoder)) { | ||||
| 2489 | while (decoder.ok() && decoder.more()) { | ||||
| 2490 | uint8_t name_type = decoder.consume_u8("name type"); | ||||
| 2491 | if (name_type & 0x80) break; // no varuint7 | ||||
| 2492 | |||||
| 2493 | uint32_t name_payload_len = decoder.consume_u32v("name payload length"); | ||||
| 2494 | if (!decoder.checkAvailable(name_payload_len)) break; | ||||
| 2495 | |||||
| 2496 | if (name_type != NameSectionKindCode::kFunctionCode) { | ||||
| 2497 | decoder.consume_bytes(name_payload_len, "name subsection payload"); | ||||
| 2498 | continue; | ||||
| 2499 | } | ||||
| 2500 | uint32_t functions_count = decoder.consume_u32v("functions count"); | ||||
| 2501 | |||||
| 2502 | for (; decoder.ok() && functions_count > 0; --functions_count) { | ||||
| 2503 | uint32_t function_index = decoder.consume_u32v("function index"); | ||||
| 2504 | WireBytesRef name = consume_string(&decoder, false, "function name"); | ||||
| 2505 | |||||
| 2506 | // Be lenient with errors in the name section: Ignore non-UTF8 names. | ||||
| 2507 | // You can even assign to the same function multiple times (last valid | ||||
| 2508 | // one wins). | ||||
| 2509 | if (decoder.ok() && validate_utf8(&decoder, name)) { | ||||
| 2510 | names->insert(std::make_pair(function_index, name)); | ||||
| 2511 | } | ||||
| 2512 | } | ||||
| 2513 | } | ||||
| 2514 | } | ||||
| 2515 | } | ||||
| 2516 | |||||
| 2517 | NameMap DecodeNameMap(base::Vector<const uint8_t> module_bytes, | ||||
| 2518 | uint8_t name_section_kind) { | ||||
| 2519 | Decoder decoder(module_bytes); | ||||
| 2520 | if (!FindNameSection(&decoder)) return NameMap{{}}; | ||||
| 2521 | |||||
| 2522 | std::vector<NameAssoc> names; | ||||
| 2523 | while (decoder.ok() && decoder.more()) { | ||||
| 2524 | uint8_t name_type = decoder.consume_u8("name type"); | ||||
| 2525 | if (name_type & 0x80) break; // no varuint7 | ||||
| 2526 | |||||
| 2527 | uint32_t name_payload_len = decoder.consume_u32v("name payload length"); | ||||
| 2528 | if (!decoder.checkAvailable(name_payload_len)) break; | ||||
| 2529 | |||||
| 2530 | if (name_type != name_section_kind) { | ||||
| 2531 | decoder.consume_bytes(name_payload_len, "name subsection payload"); | ||||
| 2532 | continue; | ||||
| 2533 | } | ||||
| 2534 | |||||
| 2535 | uint32_t count = decoder.consume_u32v("names count"); | ||||
| 2536 | for (uint32_t i = 0; i < count; i++) { | ||||
| 2537 | uint32_t index = decoder.consume_u32v("index"); | ||||
| 2538 | WireBytesRef name = consume_string(&decoder, false, "name"); | ||||
| 2539 | if (!decoder.ok()) break; | ||||
| 2540 | if (index > kMaxInt) continue; | ||||
| 2541 | if (!validate_utf8(&decoder, name)) continue; | ||||
| 2542 | names.emplace_back(static_cast<int>(index), name); | ||||
| 2543 | } | ||||
| 2544 | } | ||||
| 2545 | std::stable_sort(names.begin(), names.end(), NameAssoc::IndexLess{}); | ||||
| 2546 | return NameMap{std::move(names)}; | ||||
| 2547 | } | ||||
| 2548 | |||||
| 2549 | IndirectNameMap DecodeIndirectNameMap(base::Vector<const uint8_t> module_bytes, | ||||
| 2550 | uint8_t name_section_kind) { | ||||
| 2551 | Decoder decoder(module_bytes); | ||||
| 2552 | if (!FindNameSection(&decoder)) return IndirectNameMap{{}}; | ||||
| 2553 | |||||
| 2554 | std::vector<IndirectNameMapEntry> entries; | ||||
| 2555 | while (decoder.ok() && decoder.more()) { | ||||
| 2556 | uint8_t name_type = decoder.consume_u8("name type"); | ||||
| 2557 | if (name_type & 0x80) break; // no varuint7 | ||||
| 2558 | |||||
| 2559 | uint32_t name_payload_len = decoder.consume_u32v("name payload length"); | ||||
| 2560 | if (!decoder.checkAvailable(name_payload_len)) break; | ||||
| 2561 | |||||
| 2562 | if (name_type != name_section_kind) { | ||||
| 2563 | decoder.consume_bytes(name_payload_len, "name subsection payload"); | ||||
| 2564 | continue; | ||||
| 2565 | } | ||||
| 2566 | |||||
| 2567 | uint32_t outer_count = decoder.consume_u32v("outer count"); | ||||
| 2568 | for (uint32_t i = 0; i < outer_count; ++i) { | ||||
| 2569 | uint32_t outer_index = decoder.consume_u32v("outer index"); | ||||
| 2570 | if (outer_index > kMaxInt) continue; | ||||
| 2571 | std::vector<NameAssoc> names; | ||||
| 2572 | uint32_t inner_count = decoder.consume_u32v("inner count"); | ||||
| 2573 | for (uint32_t k = 0; k < inner_count; ++k) { | ||||
| 2574 | uint32_t inner_index = decoder.consume_u32v("inner index"); | ||||
| 2575 | WireBytesRef name = consume_string(&decoder, false, "name"); | ||||
| 2576 | if (!decoder.ok()) break; | ||||
| 2577 | if (inner_index > kMaxInt) continue; | ||||
| 2578 | // Ignore non-utf8 names. | ||||
| 2579 | if (!validate_utf8(&decoder, name)) continue; | ||||
| 2580 | names.emplace_back(static_cast<int>(inner_index), name); | ||||
| 2581 | } | ||||
| 2582 | // Use stable sort to get deterministic names (the first one declared) | ||||
| 2583 | // even in the presence of duplicates. | ||||
| 2584 | std::stable_sort(names.begin(), names.end(), NameAssoc::IndexLess{}); | ||||
| 2585 | entries.emplace_back(static_cast<int>(outer_index), std::move(names)); | ||||
| 2586 | } | ||||
| 2587 | } | ||||
| 2588 | std::stable_sort(entries.begin(), entries.end(), | ||||
| 2589 | IndirectNameMapEntry::IndexLess{}); | ||||
| 2590 | return IndirectNameMap{std::move(entries)}; | ||||
| 2591 | } | ||||
| 2592 | |||||
| 2593 | #undef TRACE | ||||
| 2594 | |||||
| 2595 | } // namespace wasm | ||||
| 2596 | } // namespace internal | ||||
| 2597 | } // namespace v8 |
| 1 | // Copyright 2015 the V8 project authors. All rights reserved. | ||||
| 2 | // Use of this source code is governed by a BSD-style license that can be | ||||
| 3 | // found in the LICENSE file. | ||||
| 4 | |||||
| 5 | #if !V8_ENABLE_WEBASSEMBLY1 | ||||
| 6 | #error This header should only be included if WebAssembly is enabled. | ||||
| 7 | #endif // !V8_ENABLE_WEBASSEMBLY | ||||
| 8 | |||||
| 9 | #ifndef V8_WASM_WASM_MODULE_H_ | ||||
| 10 | #define V8_WASM_WASM_MODULE_H_ | ||||
| 11 | |||||
| 12 | #include <map> | ||||
| 13 | #include <memory> | ||||
| 14 | |||||
| 15 | #include "src/base/optional.h" | ||||
| 16 | #include "src/base/platform/wrappers.h" | ||||
| 17 | #include "src/base/vector.h" | ||||
| 18 | #include "src/common/globals.h" | ||||
| 19 | #include "src/handles/handles.h" | ||||
| 20 | #include "src/wasm/branch-hint-map.h" | ||||
| 21 | #include "src/wasm/signature-map.h" | ||||
| 22 | #include "src/wasm/struct-types.h" | ||||
| 23 | #include "src/wasm/wasm-constants.h" | ||||
| 24 | #include "src/wasm/wasm-init-expr.h" | ||||
| 25 | #include "src/wasm/wasm-limits.h" | ||||
| 26 | |||||
| 27 | namespace v8 { | ||||
| 28 | |||||
| 29 | namespace internal { | ||||
| 30 | |||||
| 31 | class WasmModuleObject; | ||||
| 32 | |||||
| 33 | namespace wasm { | ||||
| 34 | |||||
| 35 | using WasmName = base::Vector<const char>; | ||||
| 36 | |||||
| 37 | struct AsmJsOffsets; | ||||
| 38 | class ErrorThrower; | ||||
| 39 | |||||
| 40 | // Reference to a string in the wire bytes. | ||||
| 41 | class WireBytesRef { | ||||
| 42 | public: | ||||
| 43 | WireBytesRef() : WireBytesRef(0, 0) {} | ||||
| 44 | WireBytesRef(uint32_t offset, uint32_t length) | ||||
| 45 | : offset_(offset), length_(length) { | ||||
| 46 | DCHECK_IMPLIES(offset_ == 0, length_ == 0)((void) 0); | ||||
| 47 | DCHECK_LE(offset_, offset_ + length_)((void) 0); // no uint32_t overflow. | ||||
| 48 | } | ||||
| 49 | |||||
| 50 | uint32_t offset() const { return offset_; } | ||||
| 51 | uint32_t length() const { return length_; } | ||||
| 52 | uint32_t end_offset() const { return offset_ + length_; } | ||||
| 53 | bool is_empty() const { return length_ == 0; } | ||||
| 54 | bool is_set() const { return offset_ != 0; } | ||||
| 55 | |||||
| 56 | private: | ||||
| 57 | uint32_t offset_; | ||||
| 58 | uint32_t length_; | ||||
| 59 | }; | ||||
| 60 | |||||
| 61 | // Static representation of a wasm function. | ||||
| 62 | struct WasmFunction { | ||||
| 63 | const FunctionSig* sig; // signature of the function. | ||||
| 64 | uint32_t func_index; // index into the function table. | ||||
| 65 | uint32_t sig_index; // index into the signature table. | ||||
| 66 | WireBytesRef code; // code of this function. | ||||
| 67 | // Required number of slots in a feedback vector. Marked {mutable} because | ||||
| 68 | // this is computed late (by Liftoff compilation), when the rest of the | ||||
| 69 | // {WasmFunction} is typically considered {const}. | ||||
| 70 | mutable int feedback_slots; | ||||
| 71 | bool imported; | ||||
| 72 | bool exported; | ||||
| 73 | bool declared; | ||||
| 74 | }; | ||||
| 75 | |||||
| 76 | // A representation of a constant expression. The most common expression types | ||||
| 77 | // are hard-coded, while the rest are represented as a {WireBytesRef}. | ||||
| 78 | class ConstantExpression { | ||||
| 79 | public: | ||||
| 80 | enum Kind { | ||||
| 81 | kEmpty, | ||||
| 82 | kI32Const, | ||||
| 83 | kRefNull, | ||||
| 84 | kRefFunc, | ||||
| 85 | kWireBytesRef, | ||||
| 86 | kLastKind = kWireBytesRef | ||||
| 87 | }; | ||||
| 88 | |||||
| 89 | union Value { | ||||
| 90 | int32_t i32_value; | ||||
| 91 | uint32_t index_or_offset; | ||||
| 92 | HeapType::Representation repr; | ||||
| 93 | }; | ||||
| 94 | |||||
| 95 | ConstantExpression() : bit_field_(KindField::encode(kEmpty)) {} | ||||
| 96 | |||||
| 97 | static ConstantExpression I32Const(int32_t value) { | ||||
| 98 | return ConstantExpression(ValueField::encode(value) | | ||||
| 99 | KindField::encode(kI32Const)); | ||||
| 100 | } | ||||
| 101 | static ConstantExpression RefFunc(uint32_t index) { | ||||
| 102 | return ConstantExpression(ValueField::encode(index) | | ||||
| 103 | KindField::encode(kRefFunc)); | ||||
| 104 | } | ||||
| 105 | static ConstantExpression RefNull(HeapType::Representation repr) { | ||||
| 106 | return ConstantExpression(ValueField::encode(repr) | | ||||
| 107 | KindField::encode(kRefNull)); | ||||
| 108 | } | ||||
| 109 | static ConstantExpression WireBytes(uint32_t offset, uint32_t length) { | ||||
| 110 | return ConstantExpression(OffsetField::encode(offset) | | ||||
| 111 | LengthField::encode(length) | | ||||
| 112 | KindField::encode(kWireBytesRef)); | ||||
| 113 | } | ||||
| 114 | |||||
| 115 | Kind kind() const { return KindField::decode(bit_field_); } | ||||
| 116 | |||||
| 117 | bool is_set() const { return kind() != kEmpty; } | ||||
| 118 | |||||
| 119 | uint32_t index() const { | ||||
| 120 | DCHECK_EQ(kind(), kRefFunc)((void) 0); | ||||
| 121 | return ValueField::decode(bit_field_); | ||||
| 122 | } | ||||
| 123 | |||||
| 124 | HeapType::Representation repr() const { | ||||
| 125 | DCHECK_EQ(kind(), kRefNull)((void) 0); | ||||
| 126 | return static_cast<HeapType::Representation>( | ||||
| 127 | ValueField::decode(bit_field_)); | ||||
| 128 | } | ||||
| 129 | |||||
| 130 | int32_t i32_value() const { | ||||
| 131 | DCHECK_EQ(kind(), kI32Const)((void) 0); | ||||
| 132 | return ValueField::decode(bit_field_); | ||||
| 133 | } | ||||
| 134 | |||||
| 135 | WireBytesRef wire_bytes_ref() const { | ||||
| 136 | DCHECK_EQ(kind(), kWireBytesRef)((void) 0); | ||||
| 137 | return WireBytesRef(OffsetField::decode(bit_field_), | ||||
| 138 | LengthField::decode(bit_field_)); | ||||
| 139 | } | ||||
| 140 | |||||
| 141 | private: | ||||
| 142 | static constexpr int kValueBits = 32; | ||||
| 143 | static constexpr int kLengthBits = 30; | ||||
| 144 | static constexpr int kOffsetBits = 30; | ||||
| 145 | static constexpr int kKindBits = 3; | ||||
| 146 | |||||
| 147 | // There are two possible combinations of fields: offset + length + kind if | ||||
| 148 | // kind = kWireBytesRef, or value + kind for anything else. | ||||
| 149 | using ValueField = base::BitField<uint32_t, 0, kValueBits, uint64_t>; | ||||
| 150 | using OffsetField = base::BitField<uint32_t, 0, kOffsetBits, uint64_t>; | ||||
| 151 | using LengthField = OffsetField::Next<uint32_t, kLengthBits>; | ||||
| 152 | using KindField = LengthField::Next<Kind, kKindBits>; | ||||
| 153 | |||||
| 154 | // Make sure we reserve enough bits for a {WireBytesRef}'s length and offset. | ||||
| 155 | STATIC_ASSERT(kV8MaxWasmModuleSize <= LengthField::kMax + 1)static_assert(kV8MaxWasmModuleSize <= LengthField::kMax + 1 , "kV8MaxWasmModuleSize <= LengthField::kMax + 1"); | ||||
| 156 | STATIC_ASSERT(kV8MaxWasmModuleSize <= OffsetField::kMax + 1)static_assert(kV8MaxWasmModuleSize <= OffsetField::kMax + 1 , "kV8MaxWasmModuleSize <= OffsetField::kMax + 1"); | ||||
| 157 | // Make sure kind fits in kKindBits. | ||||
| 158 | STATIC_ASSERT(kLastKind <= KindField::kMax + 1)static_assert(kLastKind <= KindField::kMax + 1, "kLastKind <= KindField::kMax + 1" ); | ||||
| 159 | |||||
| 160 | explicit ConstantExpression(uint64_t bit_field) : bit_field_(bit_field) {} | ||||
| 161 | |||||
| 162 | uint64_t bit_field_; | ||||
| 163 | }; | ||||
| 164 | |||||
| 165 | // We want to keep {ConstantExpression} small to reduce memory usage during | ||||
| 166 | // compilation/instantiation. | ||||
| 167 | STATIC_ASSERT(sizeof(ConstantExpression) <= 8)static_assert(sizeof(ConstantExpression) <= 8, "sizeof(ConstantExpression) <= 8" ); | ||||
| 168 | |||||
| 169 | // Static representation of a wasm global variable. | ||||
| 170 | struct WasmGlobal { | ||||
| 171 | ValueType type; // type of the global. | ||||
| 172 | bool mutability; // {true} if mutable. | ||||
| 173 | ConstantExpression init; // the initialization expression of the global. | ||||
| 174 | union { | ||||
| 175 | // Index of imported mutable global. | ||||
| 176 | uint32_t index; | ||||
| 177 | // Offset into global memory (if not imported & mutable). Expressed in bytes | ||||
| 178 | // for value-typed globals, and in tagged words for reference-typed globals. | ||||
| 179 | uint32_t offset; | ||||
| 180 | }; | ||||
| 181 | bool imported; // true if imported. | ||||
| 182 | bool exported; // true if exported. | ||||
| 183 | }; | ||||
| 184 | |||||
| 185 | // Note: An exception tag signature only uses the params portion of a function | ||||
| 186 | // signature. | ||||
| 187 | using WasmTagSig = FunctionSig; | ||||
| 188 | |||||
| 189 | // Static representation of a wasm tag type. | ||||
| 190 | struct WasmTag { | ||||
| 191 | explicit WasmTag(const WasmTagSig* sig) : sig(sig) {} | ||||
| 192 | const FunctionSig* ToFunctionSig() const { return sig; } | ||||
| 193 | |||||
| 194 | const WasmTagSig* sig; // type signature of the tag. | ||||
| 195 | }; | ||||
| 196 | |||||
| 197 | // Static representation of a wasm data segment. | ||||
| 198 | struct WasmDataSegment { | ||||
| 199 | // Construct an active segment. | ||||
| 200 | explicit WasmDataSegment(ConstantExpression dest_addr) | ||||
| 201 | : dest_addr(dest_addr), active(true) {} | ||||
| 202 | |||||
| 203 | // Construct a passive segment, which has no dest_addr. | ||||
| 204 | WasmDataSegment() : active(false) {} | ||||
| 205 | |||||
| 206 | ConstantExpression dest_addr; // destination memory address of the data. | ||||
| 207 | WireBytesRef source; // start offset in the module bytes. | ||||
| 208 | bool active = true; // true if copied automatically during instantiation. | ||||
| 209 | }; | ||||
| 210 | |||||
| 211 | // Static representation of wasm element segment (table initializer). | ||||
| 212 | struct WasmElemSegment { | ||||
| 213 | enum Status { | ||||
| 214 | kStatusActive, // copied automatically during instantiation. | ||||
| 215 | kStatusPassive, // copied explicitly after instantiation. | ||||
| 216 | kStatusDeclarative // purely declarative and never copied. | ||||
| 217 | }; | ||||
| 218 | enum ElementType { kFunctionIndexElements, kExpressionElements }; | ||||
| 219 | |||||
| 220 | // Construct an active segment. | ||||
| 221 | WasmElemSegment(ValueType type, uint32_t table_index, | ||||
| 222 | ConstantExpression offset, ElementType element_type) | ||||
| 223 | : status(kStatusActive), | ||||
| 224 | type(type), | ||||
| 225 | table_index(table_index), | ||||
| 226 | offset(std::move(offset)), | ||||
| 227 | element_type(element_type) {} | ||||
| 228 | |||||
| 229 | // Construct a passive or declarative segment, which has no table index or | ||||
| 230 | // offset. | ||||
| 231 | WasmElemSegment(ValueType type, Status status, ElementType element_type) | ||||
| 232 | : status(status), type(type), table_index(0), element_type(element_type) { | ||||
| 233 | DCHECK_NE(status, kStatusActive)((void) 0); | ||||
| 234 | } | ||||
| 235 | |||||
| 236 | // Default constructor. Constucts an invalid segment. | ||||
| 237 | WasmElemSegment() | ||||
| 238 | : status(kStatusActive), | ||||
| 239 | type(kWasmBottom), | ||||
| 240 | table_index(0), | ||||
| 241 | element_type(kFunctionIndexElements) {} | ||||
| 242 | |||||
| 243 | WasmElemSegment(const WasmElemSegment&) = delete; | ||||
| 244 | WasmElemSegment(WasmElemSegment&&) V8_NOEXCEPTnoexcept = default; | ||||
| 245 | WasmElemSegment& operator=(const WasmElemSegment&) = delete; | ||||
| 246 | WasmElemSegment& operator=(WasmElemSegment&&) V8_NOEXCEPTnoexcept = default; | ||||
| 247 | |||||
| 248 | Status status; | ||||
| 249 | ValueType type; | ||||
| 250 | uint32_t table_index; | ||||
| 251 | ConstantExpression offset; | ||||
| 252 | ElementType element_type; | ||||
| 253 | std::vector<ConstantExpression> entries; | ||||
| 254 | }; | ||||
| 255 | |||||
| 256 | // Static representation of a wasm import. | ||||
| 257 | struct WasmImport { | ||||
| 258 | WireBytesRef module_name; // module name. | ||||
| 259 | WireBytesRef field_name; // import name. | ||||
| 260 | ImportExportKindCode kind; // kind of the import. | ||||
| 261 | uint32_t index; // index into the respective space. | ||||
| 262 | }; | ||||
| 263 | |||||
| 264 | // Static representation of a wasm export. | ||||
| 265 | struct WasmExport { | ||||
| 266 | WireBytesRef name; // exported name. | ||||
| 267 | ImportExportKindCode kind; // kind of the export. | ||||
| 268 | uint32_t index; // index into the respective space. | ||||
| 269 | }; | ||||
| 270 | |||||
| 271 | enum class WasmCompilationHintStrategy : uint8_t { | ||||
| 272 | kDefault = 0, | ||||
| 273 | kLazy = 1, | ||||
| 274 | kEager = 2, | ||||
| 275 | kLazyBaselineEagerTopTier = 3, | ||||
| 276 | }; | ||||
| 277 | |||||
| 278 | enum class WasmCompilationHintTier : uint8_t { | ||||
| 279 | kDefault = 0, | ||||
| 280 | kBaseline = 1, | ||||
| 281 | kOptimized = 2, | ||||
| 282 | }; | ||||
| 283 | |||||
| 284 | // Static representation of a wasm compilation hint | ||||
| 285 | struct WasmCompilationHint { | ||||
| 286 | WasmCompilationHintStrategy strategy; | ||||
| 287 | WasmCompilationHintTier baseline_tier; | ||||
| 288 | WasmCompilationHintTier top_tier; | ||||
| 289 | }; | ||||
| 290 | |||||
| 291 | enum ModuleOrigin : uint8_t { | ||||
| 292 | kWasmOrigin, | ||||
| 293 | kAsmJsSloppyOrigin, | ||||
| 294 | kAsmJsStrictOrigin | ||||
| 295 | }; | ||||
| 296 | |||||
| 297 | #define SELECT_WASM_COUNTER(counters, origin, prefix, suffix)((origin) == kWasmOrigin ? (counters)->prefix_wasm_suffix( ) : (counters)->prefix_asm_suffix()) \ | ||||
| 298 | ((origin) == kWasmOrigin ? (counters)->prefix##_wasm_##suffix() \ | ||||
| 299 | : (counters)->prefix##_asm_##suffix()) | ||||
| 300 | |||||
| 301 | struct ModuleWireBytes; | ||||
| 302 | |||||
| 303 | class V8_EXPORT_PRIVATE LazilyGeneratedNames { | ||||
| 304 | public: | ||||
| 305 | WireBytesRef LookupFunctionName(const ModuleWireBytes& wire_bytes, | ||||
| 306 | uint32_t function_index) const; | ||||
| 307 | |||||
| 308 | void AddForTesting(int function_index, WireBytesRef name); | ||||
| 309 | |||||
| 310 | private: | ||||
| 311 | // {function_names_} are populated lazily after decoding, and | ||||
| 312 | // therefore need a mutex to protect concurrent modifications | ||||
| 313 | // from multiple {WasmModuleObject}. | ||||
| 314 | mutable base::Mutex mutex_; | ||||
| 315 | mutable std::unique_ptr<std::unordered_map<uint32_t, WireBytesRef>> | ||||
| 316 | function_names_; | ||||
| 317 | }; | ||||
| 318 | |||||
| 319 | class V8_EXPORT_PRIVATE AsmJsOffsetInformation { | ||||
| 320 | public: | ||||
| 321 | explicit AsmJsOffsetInformation(base::Vector<const byte> encoded_offsets); | ||||
| 322 | |||||
| 323 | // Destructor defined in wasm-module.cc, where the definition of | ||||
| 324 | // {AsmJsOffsets} is available. | ||||
| 325 | ~AsmJsOffsetInformation(); | ||||
| 326 | |||||
| 327 | int GetSourcePosition(int func_index, int byte_offset, | ||||
| 328 | bool is_at_number_conversion); | ||||
| 329 | |||||
| 330 | std::pair<int, int> GetFunctionOffsets(int func_index); | ||||
| 331 | |||||
| 332 | private: | ||||
| 333 | void EnsureDecodedOffsets(); | ||||
| 334 | |||||
| 335 | // The offset information table is decoded lazily, hence needs to be | ||||
| 336 | // protected against concurrent accesses. | ||||
| 337 | // Exactly one of the two fields below will be set at a time. | ||||
| 338 | mutable base::Mutex mutex_; | ||||
| 339 | |||||
| 340 | // Holds the encoded offset table bytes. | ||||
| 341 | base::OwnedVector<const uint8_t> encoded_offsets_; | ||||
| 342 | |||||
| 343 | // Holds the decoded offset table. | ||||
| 344 | std::unique_ptr<AsmJsOffsets> decoded_offsets_; | ||||
| 345 | }; | ||||
| 346 | |||||
| 347 | // Used as the supertype for a type at the top of the type hierarchy. | ||||
| 348 | constexpr uint32_t kNoSuperType = std::numeric_limits<uint32_t>::max(); | ||||
| 349 | |||||
| 350 | struct TypeDefinition { | ||||
| 351 | enum Kind { kFunction, kStruct, kArray }; | ||||
| 352 | |||||
| 353 | TypeDefinition(const FunctionSig* sig, uint32_t supertype) | ||||
| 354 | : function_sig(sig), supertype(supertype), kind(kFunction) {} | ||||
| 355 | TypeDefinition(const StructType* type, uint32_t supertype) | ||||
| 356 | : struct_type(type), supertype(supertype), kind(kStruct) {} | ||||
| 357 | TypeDefinition(const ArrayType* type, uint32_t supertype) | ||||
| 358 | : array_type(type), supertype(supertype), kind(kArray) {} | ||||
| 359 | TypeDefinition() | ||||
| 360 | : function_sig(nullptr), supertype(kNoSuperType), kind(kFunction) {} | ||||
| 361 | |||||
| 362 | union { | ||||
| 363 | const FunctionSig* function_sig; | ||||
| 364 | const StructType* struct_type; | ||||
| 365 | const ArrayType* array_type; | ||||
| 366 | }; | ||||
| 367 | |||||
| 368 | bool operator==(const TypeDefinition& other) const { | ||||
| 369 | if (supertype != other.supertype || kind != other.kind) { | ||||
| 370 | return false; | ||||
| 371 | } | ||||
| 372 | switch (kind) { | ||||
| 373 | case kFunction: | ||||
| 374 | return *function_sig == *other.function_sig; | ||||
| 375 | case kStruct: | ||||
| 376 | return *struct_type == *other.struct_type; | ||||
| 377 | case kArray: | ||||
| 378 | return *array_type == *other.array_type; | ||||
| 379 | } | ||||
| 380 | } | ||||
| 381 | |||||
| 382 | bool operator!=(const TypeDefinition& other) const { | ||||
| 383 | return !(*this == other); | ||||
| 384 | } | ||||
| 385 | |||||
| 386 | uint32_t supertype; | ||||
| 387 | Kind kind; | ||||
| 388 | }; | ||||
| 389 | |||||
| 390 | struct V8_EXPORT_PRIVATE WasmDebugSymbols { | ||||
| 391 | enum class Type { None, SourceMap, EmbeddedDWARF, ExternalDWARF }; | ||||
| 392 | Type type = Type::None; | ||||
| 393 | WireBytesRef external_url; | ||||
| 394 | }; | ||||
| 395 | |||||
| 396 | struct CallSiteFeedback { | ||||
| 397 | int function_index; | ||||
| 398 | int absolute_call_frequency; | ||||
| 399 | }; | ||||
| 400 | struct FunctionTypeFeedback { | ||||
| 401 | std::vector<CallSiteFeedback> feedback_vector; | ||||
| 402 | std::map<WasmCodePosition, int> positions; | ||||
| 403 | int tierup_priority = 0; | ||||
| 404 | }; | ||||
| 405 | struct TypeFeedbackStorage { | ||||
| 406 | std::map<uint32_t, FunctionTypeFeedback> feedback_for_function; | ||||
| 407 | // Accesses to {feedback_for_function} are guarded by this mutex. | ||||
| 408 | base::Mutex mutex; | ||||
| 409 | }; | ||||
| 410 | |||||
| 411 | struct WasmTable; | ||||
| 412 | |||||
| 413 | // Static representation of a module. | ||||
| 414 | struct V8_EXPORT_PRIVATE WasmModule { | ||||
| 415 | std::unique_ptr<Zone> signature_zone; | ||||
| 416 | uint32_t initial_pages = 0; // initial size of the memory in 64k pages | ||||
| 417 | uint32_t maximum_pages = 0; // maximum size of the memory in 64k pages | ||||
| 418 | bool has_shared_memory = false; // true if memory is a SharedArrayBuffer | ||||
| 419 | bool has_maximum_pages = false; // true if there is a maximum memory size | ||||
| 420 | bool is_memory64 = false; // true if the memory is 64 bit | ||||
| 421 | bool has_memory = false; // true if the memory was defined or imported | ||||
| 422 | bool mem_export = false; // true if the memory is exported | ||||
| 423 | int start_function_index = -1; // start function, >= 0 if any | ||||
| 424 | |||||
| 425 | // Size of the buffer required for all globals that are not imported and | ||||
| 426 | // mutable. | ||||
| 427 | uint32_t untagged_globals_buffer_size = 0; | ||||
| 428 | uint32_t tagged_globals_buffer_size = 0; | ||||
| 429 | uint32_t num_imported_mutable_globals = 0; | ||||
| 430 | uint32_t num_imported_functions = 0; | ||||
| 431 | uint32_t num_imported_tables = 0; | ||||
| 432 | uint32_t num_declared_functions = 0; // excluding imported | ||||
| 433 | uint32_t num_exported_functions = 0; | ||||
| 434 | uint32_t num_declared_data_segments = 0; // From the DataCount section. | ||||
| 435 | // Position and size of the code section (payload only, i.e. without section | ||||
| 436 | // ID and length). | ||||
| 437 | WireBytesRef code = {0, 0}; | ||||
| 438 | WireBytesRef name = {0, 0}; | ||||
| 439 | |||||
| 440 | void add_type(TypeDefinition type) { | ||||
| 441 | types.push_back(type); | ||||
| 442 | uint32_t canonical_id = type.kind
| ||||
| 443 | ? signature_map.FindOrInsert(*type.function_sig) | ||||
| |||||
| 444 | : 0; | ||||
| 445 | canonicalized_type_ids.push_back(canonical_id); | ||||
| 446 | // Canonical type will be computed later. | ||||
| 447 | isorecursive_canonical_type_ids.push_back(kNoSuperType); | ||||
| 448 | } | ||||
| 449 | |||||
| 450 | bool has_type(uint32_t index) const { return index < types.size(); } | ||||
| 451 | |||||
| 452 | void add_signature(const FunctionSig* sig, uint32_t supertype) { | ||||
| 453 | DCHECK_NOT_NULL(sig)((void) 0); | ||||
| 454 | add_type(TypeDefinition(sig, supertype)); | ||||
| 455 | } | ||||
| 456 | bool has_signature(uint32_t index) const { | ||||
| 457 | return index < types.size() && | ||||
| 458 | types[index].kind == TypeDefinition::kFunction; | ||||
| 459 | } | ||||
| 460 | const FunctionSig* signature(uint32_t index) const { | ||||
| 461 | DCHECK(has_signature(index))((void) 0); | ||||
| 462 | return types[index].function_sig; | ||||
| 463 | } | ||||
| 464 | |||||
| 465 | void add_struct_type(const StructType* type, uint32_t supertype) { | ||||
| 466 | DCHECK_NOT_NULL(type)((void) 0); | ||||
| 467 | add_type(TypeDefinition(type, supertype)); | ||||
| 468 | } | ||||
| 469 | bool has_struct(uint32_t index) const { | ||||
| 470 | return index < types.size() && types[index].kind == TypeDefinition::kStruct; | ||||
| 471 | } | ||||
| 472 | const StructType* struct_type(uint32_t index) const { | ||||
| 473 | DCHECK(has_struct(index))((void) 0); | ||||
| 474 | return types[index].struct_type; | ||||
| 475 | } | ||||
| 476 | |||||
| 477 | void add_array_type(const ArrayType* type, uint32_t supertype) { | ||||
| 478 | DCHECK_NOT_NULL(type)((void) 0); | ||||
| 479 | add_type(TypeDefinition(type, supertype)); | ||||
| 480 | } | ||||
| 481 | bool has_array(uint32_t index) const { | ||||
| 482 | return index < types.size() && types[index].kind == TypeDefinition::kArray; | ||||
| 483 | } | ||||
| 484 | const ArrayType* array_type(uint32_t index) const { | ||||
| 485 | DCHECK(has_array(index))((void) 0); | ||||
| 486 | return types[index].array_type; | ||||
| 487 | } | ||||
| 488 | |||||
| 489 | uint32_t supertype(uint32_t index) const { | ||||
| 490 | DCHECK(index < types.size())((void) 0); | ||||
| 491 | return types[index].supertype; | ||||
| 492 | } | ||||
| 493 | bool has_supertype(uint32_t index) const { | ||||
| 494 | return supertype(index) != kNoSuperType; | ||||
| 495 | } | ||||
| 496 | |||||
| 497 | std::vector<TypeDefinition> types; // by type index | ||||
| 498 | // TODO(7748): Unify the following two arrays. | ||||
| 499 | // Maps each type index to a canonical index for purposes of call_indirect. | ||||
| 500 | std::vector<uint32_t> canonicalized_type_ids; | ||||
| 501 | // Maps each type index to its global (cross-module) canonical index as per | ||||
| 502 | // isorecursive type canonicalization. | ||||
| 503 | std::vector<uint32_t> isorecursive_canonical_type_ids; | ||||
| 504 | // Canonicalizing map for signature indexes. | ||||
| 505 | SignatureMap signature_map; | ||||
| 506 | std::vector<WasmFunction> functions; | ||||
| 507 | std::vector<WasmGlobal> globals; | ||||
| 508 | std::vector<WasmDataSegment> data_segments; | ||||
| 509 | std::vector<WasmTable> tables; | ||||
| 510 | std::vector<WasmImport> import_table; | ||||
| 511 | std::vector<WasmExport> export_table; | ||||
| 512 | std::vector<WasmTag> tags; | ||||
| 513 | std::vector<WasmElemSegment> elem_segments; | ||||
| 514 | std::vector<WasmCompilationHint> compilation_hints; | ||||
| 515 | BranchHintInfo branch_hints; | ||||
| 516 | mutable TypeFeedbackStorage type_feedback; | ||||
| 517 | |||||
| 518 | ModuleOrigin origin = kWasmOrigin; // origin of the module | ||||
| 519 | LazilyGeneratedNames lazily_generated_names; | ||||
| 520 | WasmDebugSymbols debug_symbols; | ||||
| 521 | |||||
| 522 | // Asm.js source position information. Only available for modules compiled | ||||
| 523 | // from asm.js. | ||||
| 524 | std::unique_ptr<AsmJsOffsetInformation> asm_js_offset_information; | ||||
| 525 | |||||
| 526 | explicit WasmModule(std::unique_ptr<Zone> signature_zone = nullptr); | ||||
| 527 | WasmModule(const WasmModule&) = delete; | ||||
| 528 | WasmModule& operator=(const WasmModule&) = delete; | ||||
| 529 | }; | ||||
| 530 | |||||
| 531 | // Static representation of a wasm indirect call table. | ||||
| 532 | struct WasmTable { | ||||
| 533 | MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmTable)WasmTable() = default; WasmTable(WasmTable&&) noexcept = default; WasmTable& operator=(WasmTable&&) noexcept = default; WasmTable(const WasmTable&) = delete; WasmTable & operator=(const WasmTable&) = delete; | ||||
| 534 | |||||
| 535 | // 'module' can be nullptr | ||||
| 536 | // TODO(9495): Update this function as more table types are supported, or | ||||
| 537 | // remove it completely when all reference types are allowed. | ||||
| 538 | static bool IsValidTableType(ValueType type, const WasmModule* module) { | ||||
| 539 | if (!type.is_object_reference()) return false; | ||||
| 540 | HeapType heap_type = type.heap_type(); | ||||
| 541 | return heap_type == HeapType::kFunc || heap_type == HeapType::kAny || | ||||
| 542 | (module != nullptr && heap_type.is_index() && | ||||
| 543 | module->has_signature(heap_type.ref_index())); | ||||
| 544 | } | ||||
| 545 | |||||
| 546 | ValueType type = kWasmVoid; // table type. | ||||
| 547 | uint32_t initial_size = 0; // initial table size. | ||||
| 548 | uint32_t maximum_size = 0; // maximum table size. | ||||
| 549 | bool has_maximum_size = false; // true if there is a maximum size. | ||||
| 550 | bool imported = false; // true if imported. | ||||
| 551 | bool exported = false; // true if exported. | ||||
| 552 | ConstantExpression initial_value; | ||||
| 553 | }; | ||||
| 554 | |||||
| 555 | inline bool is_asmjs_module(const WasmModule* module) { | ||||
| 556 | return module->origin != kWasmOrigin; | ||||
| 557 | } | ||||
| 558 | |||||
| 559 | size_t EstimateStoredSize(const WasmModule* module); | ||||
| 560 | |||||
| 561 | // Returns the number of possible export wrappers for a given module. | ||||
| 562 | V8_EXPORT_PRIVATE int MaxNumExportWrappers(const WasmModule* module); | ||||
| 563 | |||||
| 564 | // Returns the wrapper index for a function in {module} with signature {sig} | ||||
| 565 | // or {sig_index} and origin defined by {is_import}. | ||||
| 566 | // Prefer to use the {sig_index} consuming version, as it is much faster. | ||||
| 567 | int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig, | ||||
| 568 | bool is_import); | ||||
| 569 | int GetExportWrapperIndex(const WasmModule* module, uint32_t sig_index, | ||||
| 570 | bool is_import); | ||||
| 571 | |||||
| 572 | // Return the byte offset of the function identified by the given index. | ||||
| 573 | // The offset will be relative to the start of the module bytes. | ||||
| 574 | // Returns -1 if the function index is invalid. | ||||
| 575 | int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index); | ||||
| 576 | |||||
| 577 | // Returns the function containing the given byte offset. | ||||
| 578 | // Returns -1 if the byte offset is not contained in any | ||||
| 579 | // function of this module. | ||||
| 580 | int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset); | ||||
| 581 | |||||
| 582 | // Returns the function containing the given byte offset. | ||||
| 583 | // Will return preceding function if the byte offset is not | ||||
| 584 | // contained within a function. | ||||
| 585 | int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset); | ||||
| 586 | |||||
| 587 | // Gets the explicitly defined subtyping depth for the given type. | ||||
| 588 | // Returns 0 if the type has no explicit supertype. | ||||
| 589 | // The result is capped to {kV8MaxRttSubtypingDepth + 1}. | ||||
| 590 | // Invalid cyclic hierarchies will return -1. | ||||
| 591 | V8_EXPORT_PRIVATE int GetSubtypingDepth(const WasmModule* module, | ||||
| 592 | uint32_t type_index); | ||||
| 593 | |||||
| 594 | // Interface to the storage (wire bytes) of a wasm module. | ||||
| 595 | // It is illegal for anyone receiving a ModuleWireBytes to store pointers based | ||||
| 596 | // on module_bytes, as this storage is only guaranteed to be alive as long as | ||||
| 597 | // this struct is alive. | ||||
| 598 | struct V8_EXPORT_PRIVATE ModuleWireBytes { | ||||
| 599 | explicit ModuleWireBytes(base::Vector<const byte> module_bytes) | ||||
| 600 | : module_bytes_(module_bytes) {} | ||||
| 601 | ModuleWireBytes(const byte* start, const byte* end) | ||||
| 602 | : module_bytes_(start, static_cast<int>(end - start)) { | ||||
| 603 | DCHECK_GE(kMaxInt, end - start)((void) 0); | ||||
| 604 | } | ||||
| 605 | |||||
| 606 | // Get a string stored in the module bytes representing a name. | ||||
| 607 | WasmName GetNameOrNull(WireBytesRef ref) const; | ||||
| 608 | |||||
| 609 | // Get a string stored in the module bytes representing a function name. | ||||
| 610 | WasmName GetNameOrNull(const WasmFunction* function, | ||||
| 611 | const WasmModule* module) const; | ||||
| 612 | |||||
| 613 | // Checks the given reference is contained within the module bytes. | ||||
| 614 | bool BoundsCheck(WireBytesRef ref) const { | ||||
| 615 | uint32_t size = static_cast<uint32_t>(module_bytes_.length()); | ||||
| 616 | return ref.offset() <= size && ref.length() <= size - ref.offset(); | ||||
| 617 | } | ||||
| 618 | |||||
| 619 | base::Vector<const byte> GetFunctionBytes( | ||||
| 620 | const WasmFunction* function) const { | ||||
| 621 | return module_bytes_.SubVector(function->code.offset(), | ||||
| 622 | function->code.end_offset()); | ||||
| 623 | } | ||||
| 624 | |||||
| 625 | base::Vector<const byte> module_bytes() const { return module_bytes_; } | ||||
| 626 | const byte* start() const { return module_bytes_.begin(); } | ||||
| 627 | const byte* end() const { return module_bytes_.end(); } | ||||
| 628 | size_t length() const { return module_bytes_.length(); } | ||||
| 629 | |||||
| 630 | private: | ||||
| 631 | base::Vector<const byte> module_bytes_; | ||||
| 632 | }; | ||||
| 633 | |||||
| 634 | // A helper for printing out the names of functions. | ||||
| 635 | struct WasmFunctionName { | ||||
| 636 | WasmFunctionName(const WasmFunction* function, WasmName name) | ||||
| 637 | : function_(function), name_(name) {} | ||||
| 638 | |||||
| 639 | const WasmFunction* function_; | ||||
| 640 | const WasmName name_; | ||||
| 641 | }; | ||||
| 642 | |||||
| 643 | std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name); | ||||
| 644 | |||||
| 645 | V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate, | ||||
| 646 | Handle<Context> context); | ||||
| 647 | |||||
| 648 | Handle<JSObject> GetTypeForFunction(Isolate* isolate, const FunctionSig* sig, | ||||
| 649 | bool for_exception = false); | ||||
| 650 | Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable, | ||||
| 651 | ValueType type); | ||||
| 652 | Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size, | ||||
| 653 | base::Optional<uint32_t> max_size, | ||||
| 654 | bool shared); | ||||
| 655 | Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type, | ||||
| 656 | uint32_t min_size, | ||||
| 657 | base::Optional<uint32_t> max_size); | ||||
| 658 | Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module); | ||||
| 659 | Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module); | ||||
| 660 | Handle<JSArray> GetCustomSections(Isolate* isolate, | ||||
| 661 | Handle<WasmModuleObject> module, | ||||
| 662 | Handle<String> name, ErrorThrower* thrower); | ||||
| 663 | |||||
| 664 | // Get the source position from a given function index and byte offset, | ||||
| 665 | // for either asm.js or pure Wasm modules. | ||||
| 666 | int GetSourcePosition(const WasmModule*, uint32_t func_index, | ||||
| 667 | uint32_t byte_offset, bool is_at_number_conversion); | ||||
| 668 | |||||
| 669 | // Translate function index to the index relative to the first declared (i.e. | ||||
| 670 | // non-imported) function. | ||||
| 671 | inline int declared_function_index(const WasmModule* module, int func_index) { | ||||
| 672 | DCHECK_LE(module->num_imported_functions, func_index)((void) 0); | ||||
| 673 | int declared_idx = func_index - module->num_imported_functions; | ||||
| 674 | DCHECK_GT(module->num_declared_functions, declared_idx)((void) 0); | ||||
| 675 | return declared_idx; | ||||
| 676 | } | ||||
| 677 | |||||
| 678 | // TruncatedUserString makes it easy to output names up to a certain length, and | ||||
| 679 | // output a truncation followed by '...' if they exceed a limit. | ||||
| 680 | // Use like this: | ||||
| 681 | // TruncatedUserString<> name (pc, len); | ||||
| 682 | // printf("... %.*s ...", name.length(), name.start()) | ||||
| 683 | template <int kMaxLen = 50> | ||||
| 684 | class TruncatedUserString { | ||||
| 685 | static_assert(kMaxLen >= 4, "minimum length is 4 (length of '...' plus one)"); | ||||
| 686 | |||||
| 687 | public: | ||||
| 688 | template <typename T> | ||||
| 689 | explicit TruncatedUserString(base::Vector<T> name) | ||||
| 690 | : TruncatedUserString(name.begin(), name.length()) {} | ||||
| 691 | |||||
| 692 | TruncatedUserString(const byte* start, size_t len) | ||||
| 693 | : TruncatedUserString(reinterpret_cast<const char*>(start), len) {} | ||||
| 694 | |||||
| 695 | TruncatedUserString(const char* start, size_t len) | ||||
| 696 | : start_(start), length_(std::min(kMaxLen, static_cast<int>(len))) { | ||||
| 697 | if (len > static_cast<size_t>(kMaxLen)) { | ||||
| 698 | memcpy(buffer_, start, kMaxLen - 3); | ||||
| 699 | memset(buffer_ + kMaxLen - 3, '.', 3); | ||||
| 700 | start_ = buffer_; | ||||
| 701 | } | ||||
| 702 | } | ||||
| 703 | |||||
| 704 | const char* start() const { return start_; } | ||||
| 705 | |||||
| 706 | int length() const { return length_; } | ||||
| 707 | |||||
| 708 | private: | ||||
| 709 | const char* start_; | ||||
| 710 | const int length_; | ||||
| 711 | char buffer_[kMaxLen]; | ||||
| 712 | }; | ||||
| 713 | |||||
| 714 | // Print the signature into the given {buffer}, using {delimiter} as separator | ||||
| 715 | // between parameter types and return types. If {buffer} is non-empty, it will | ||||
| 716 | // be null-terminated, even if the signature is cut off. Returns the number of | ||||
| 717 | // characters written, excluding the terminating null-byte. | ||||
| 718 | size_t PrintSignature(base::Vector<char> buffer, const wasm::FunctionSig*, | ||||
| 719 | char delimiter = ':'); | ||||
| 720 | |||||
| 721 | } // namespace wasm | ||||
| 722 | } // namespace internal | ||||
| 723 | } // namespace v8 | ||||
| 724 | |||||
| 725 | #endif // V8_WASM_WASM_MODULE_H_ |