• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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/wasm-serialization.h"
6 
7 #include "src/codegen/assembler-inl.h"
8 #include "src/codegen/external-reference-table.h"
9 #include "src/objects/objects-inl.h"
10 #include "src/objects/objects.h"
11 #include "src/runtime/runtime.h"
12 #include "src/snapshot/code-serializer.h"
13 #include "src/utils/ostreams.h"
14 #include "src/utils/utils.h"
15 #include "src/utils/version.h"
16 #include "src/wasm/code-space-access.h"
17 #include "src/wasm/function-compiler.h"
18 #include "src/wasm/module-compiler.h"
19 #include "src/wasm/module-decoder.h"
20 #include "src/wasm/wasm-code-manager.h"
21 #include "src/wasm/wasm-module.h"
22 #include "src/wasm/wasm-objects-inl.h"
23 #include "src/wasm/wasm-objects.h"
24 #include "src/wasm/wasm-result.h"
25 
26 namespace v8 {
27 namespace internal {
28 namespace wasm {
29 
30 namespace {
31 
32 // TODO(bbudge) Try to unify the various implementations of readers and writers
33 // in Wasm, e.g. StreamProcessor and ZoneBuffer, with these.
34 class Writer {
35  public:
Writer(Vector<byte> buffer)36   explicit Writer(Vector<byte> buffer)
37       : start_(buffer.begin()), end_(buffer.end()), pos_(buffer.begin()) {}
38 
bytes_written() const39   size_t bytes_written() const { return pos_ - start_; }
current_location() const40   byte* current_location() const { return pos_; }
current_size() const41   size_t current_size() const { return end_ - pos_; }
current_buffer() const42   Vector<byte> current_buffer() const {
43     return {current_location(), current_size()};
44   }
45 
46   template <typename T>
Write(const T & value)47   void Write(const T& value) {
48     DCHECK_GE(current_size(), sizeof(T));
49     WriteUnalignedValue(reinterpret_cast<Address>(current_location()), value);
50     pos_ += sizeof(T);
51     if (FLAG_trace_wasm_serialization) {
52       StdoutStream{} << "wrote: " << static_cast<size_t>(value)
53                      << " sized: " << sizeof(T) << std::endl;
54     }
55   }
56 
WriteVector(const Vector<const byte> v)57   void WriteVector(const Vector<const byte> v) {
58     DCHECK_GE(current_size(), v.size());
59     if (v.size() > 0) {
60       memcpy(current_location(), v.begin(), v.size());
61       pos_ += v.size();
62     }
63     if (FLAG_trace_wasm_serialization) {
64       StdoutStream{} << "wrote vector of " << v.size() << " elements"
65                      << std::endl;
66     }
67   }
68 
Skip(size_t size)69   void Skip(size_t size) { pos_ += size; }
70 
71  private:
72   byte* const start_;
73   byte* const end_;
74   byte* pos_;
75 };
76 
77 class Reader {
78  public:
Reader(Vector<const byte> buffer)79   explicit Reader(Vector<const byte> buffer)
80       : start_(buffer.begin()), end_(buffer.end()), pos_(buffer.begin()) {}
81 
bytes_read() const82   size_t bytes_read() const { return pos_ - start_; }
current_location() const83   const byte* current_location() const { return pos_; }
current_size() const84   size_t current_size() const { return end_ - pos_; }
current_buffer() const85   Vector<const byte> current_buffer() const {
86     return {current_location(), current_size()};
87   }
88 
89   template <typename T>
Read()90   T Read() {
91     DCHECK_GE(current_size(), sizeof(T));
92     T value =
93         ReadUnalignedValue<T>(reinterpret_cast<Address>(current_location()));
94     pos_ += sizeof(T);
95     if (FLAG_trace_wasm_serialization) {
96       StdoutStream{} << "read: " << static_cast<size_t>(value)
97                      << " sized: " << sizeof(T) << std::endl;
98     }
99     return value;
100   }
101 
102   template <typename T>
ReadVector(size_t size)103   Vector<const T> ReadVector(size_t size) {
104     DCHECK_GE(current_size(), size);
105     Vector<const byte> bytes{pos_, size * sizeof(T)};
106     pos_ += size * sizeof(T);
107     if (FLAG_trace_wasm_serialization) {
108       StdoutStream{} << "read vector of " << size << " elements of size "
109                      << sizeof(T) << " (total size " << size * sizeof(T) << ")"
110                      << std::endl;
111     }
112     return Vector<const T>::cast(bytes);
113   }
114 
Skip(size_t size)115   void Skip(size_t size) { pos_ += size; }
116 
117  private:
118   const byte* const start_;
119   const byte* const end_;
120   const byte* pos_;
121 };
122 
WriteHeader(Writer * writer)123 void WriteHeader(Writer* writer) {
124   writer->Write(SerializedData::kMagicNumber);
125   writer->Write(Version::Hash());
126   writer->Write(static_cast<uint32_t>(CpuFeatures::SupportedFeatures()));
127   writer->Write(FlagList::Hash());
128   DCHECK_EQ(WasmSerializer::kHeaderSize, writer->bytes_written());
129 }
130 
131 // On Intel, call sites are encoded as a displacement. For linking and for
132 // serialization/deserialization, we want to store/retrieve a tag (the function
133 // index). On Intel, that means accessing the raw displacement.
134 // On ARM64, call sites are encoded as either a literal load or a direct branch.
135 // Other platforms simply require accessing the target address.
SetWasmCalleeTag(RelocInfo * rinfo,uint32_t tag)136 void SetWasmCalleeTag(RelocInfo* rinfo, uint32_t tag) {
137 #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
138   DCHECK(rinfo->HasTargetAddressAddress());
139   DCHECK(!RelocInfo::IsCompressedEmbeddedObject(rinfo->rmode()));
140   WriteUnalignedValue(rinfo->target_address_address(), tag);
141 #elif V8_TARGET_ARCH_ARM64
142   Instruction* instr = reinterpret_cast<Instruction*>(rinfo->pc());
143   if (instr->IsLdrLiteralX()) {
144     WriteUnalignedValue(rinfo->constant_pool_entry_address(),
145                         static_cast<Address>(tag));
146   } else {
147     DCHECK(instr->IsBranchAndLink() || instr->IsUnconditionalBranch());
148     instr->SetBranchImmTarget(
149         reinterpret_cast<Instruction*>(rinfo->pc() + tag * kInstrSize));
150   }
151 #else
152   Address addr = static_cast<Address>(tag);
153   if (rinfo->rmode() == RelocInfo::EXTERNAL_REFERENCE) {
154     rinfo->set_target_external_reference(addr, SKIP_ICACHE_FLUSH);
155   } else if (rinfo->rmode() == RelocInfo::WASM_STUB_CALL) {
156     rinfo->set_wasm_stub_call_address(addr, SKIP_ICACHE_FLUSH);
157   } else {
158     rinfo->set_target_address(addr, SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
159   }
160 #endif
161 }
162 
GetWasmCalleeTag(RelocInfo * rinfo)163 uint32_t GetWasmCalleeTag(RelocInfo* rinfo) {
164 #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
165   DCHECK(!RelocInfo::IsCompressedEmbeddedObject(rinfo->rmode()));
166   return ReadUnalignedValue<uint32_t>(rinfo->target_address_address());
167 #elif V8_TARGET_ARCH_ARM64
168   Instruction* instr = reinterpret_cast<Instruction*>(rinfo->pc());
169   if (instr->IsLdrLiteralX()) {
170     return ReadUnalignedValue<uint32_t>(rinfo->constant_pool_entry_address());
171   } else {
172     DCHECK(instr->IsBranchAndLink() || instr->IsUnconditionalBranch());
173     return static_cast<uint32_t>(instr->ImmPCOffset() / kInstrSize);
174   }
175 #else
176   Address addr;
177   if (rinfo->rmode() == RelocInfo::EXTERNAL_REFERENCE) {
178     addr = rinfo->target_external_reference();
179   } else if (rinfo->rmode() == RelocInfo::WASM_STUB_CALL) {
180     addr = rinfo->wasm_stub_call_address();
181   } else {
182     addr = rinfo->target_address();
183   }
184   return static_cast<uint32_t>(addr);
185 #endif
186 }
187 
188 constexpr size_t kHeaderSize =
189     sizeof(uint32_t) +  // total wasm function count
190     sizeof(uint32_t);   // imported functions (index of first wasm function)
191 
192 constexpr size_t kCodeHeaderSize = sizeof(bool) +  // whether code is present
193                                    sizeof(int) +   // offset of constant pool
194                                    sizeof(int) +   // offset of safepoint table
195                                    sizeof(int) +   // offset of handler table
196                                    sizeof(int) +   // offset of code comments
197                                    sizeof(int) +   // unpadded binary size
198                                    sizeof(int) +   // stack slots
199                                    sizeof(int) +   // tagged parameter slots
200                                    sizeof(int) +   // code size
201                                    sizeof(int) +   // reloc size
202                                    sizeof(int) +   // source positions size
203                                    sizeof(int) +  // protected instructions size
204                                    sizeof(WasmCode::Kind) +  // code kind
205                                    sizeof(ExecutionTier);    // tier
206 
207 // A List of all isolate-independent external references. This is used to create
208 // a tag from the Address of an external reference and vice versa.
209 class ExternalReferenceList {
210  public:
211   ExternalReferenceList(const ExternalReferenceList&) = delete;
212   ExternalReferenceList& operator=(const ExternalReferenceList&) = delete;
213 
tag_from_address(Address ext_ref_address) const214   uint32_t tag_from_address(Address ext_ref_address) const {
215     auto tag_addr_less_than = [this](uint32_t tag, Address searched_addr) {
216       return external_reference_by_tag_[tag] < searched_addr;
217     };
218     auto it = std::lower_bound(std::begin(tags_ordered_by_address_),
219                                std::end(tags_ordered_by_address_),
220                                ext_ref_address, tag_addr_less_than);
221     DCHECK_NE(std::end(tags_ordered_by_address_), it);
222     uint32_t tag = *it;
223     DCHECK_EQ(address_from_tag(tag), ext_ref_address);
224     return tag;
225   }
226 
address_from_tag(uint32_t tag) const227   Address address_from_tag(uint32_t tag) const {
228     DCHECK_GT(kNumExternalReferences, tag);
229     return external_reference_by_tag_[tag];
230   }
231 
Get()232   static const ExternalReferenceList& Get() {
233     static ExternalReferenceList list;  // Lazily initialized.
234     return list;
235   }
236 
237  private:
238   // Private constructor. There will only be a single instance of this object.
ExternalReferenceList()239   ExternalReferenceList() {
240     for (uint32_t i = 0; i < kNumExternalReferences; ++i) {
241       tags_ordered_by_address_[i] = i;
242     }
243     auto addr_by_tag_less_than = [this](uint32_t a, uint32_t b) {
244       return external_reference_by_tag_[a] < external_reference_by_tag_[b];
245     };
246     std::sort(std::begin(tags_ordered_by_address_),
247               std::end(tags_ordered_by_address_), addr_by_tag_less_than);
248   }
249 
250 #define COUNT_EXTERNAL_REFERENCE(name, ...) +1
251   static constexpr uint32_t kNumExternalReferencesList =
252       EXTERNAL_REFERENCE_LIST(COUNT_EXTERNAL_REFERENCE);
253   static constexpr uint32_t kNumExternalReferencesIntrinsics =
254       FOR_EACH_INTRINSIC(COUNT_EXTERNAL_REFERENCE);
255   static constexpr uint32_t kNumExternalReferences =
256       kNumExternalReferencesList + kNumExternalReferencesIntrinsics;
257 #undef COUNT_EXTERNAL_REFERENCE
258 
259   Address external_reference_by_tag_[kNumExternalReferences] = {
260 #define EXT_REF_ADDR(name, desc) ExternalReference::name().address(),
261       EXTERNAL_REFERENCE_LIST(EXT_REF_ADDR)
262 #undef EXT_REF_ADDR
263 #define RUNTIME_ADDR(name, ...) \
264   ExternalReference::Create(Runtime::k##name).address(),
265           FOR_EACH_INTRINSIC(RUNTIME_ADDR)
266 #undef RUNTIME_ADDR
267   };
268   uint32_t tags_ordered_by_address_[kNumExternalReferences];
269 };
270 
271 static_assert(std::is_trivially_destructible<ExternalReferenceList>::value,
272               "static destructors not allowed");
273 
274 }  // namespace
275 
276 class V8_EXPORT_PRIVATE NativeModuleSerializer {
277  public:
278   NativeModuleSerializer(const NativeModule*, Vector<WasmCode* const>);
279   NativeModuleSerializer(const NativeModuleSerializer&) = delete;
280   NativeModuleSerializer& operator=(const NativeModuleSerializer&) = delete;
281 
282   size_t Measure() const;
283   bool Write(Writer* writer);
284 
285  private:
286   size_t MeasureCode(const WasmCode*) const;
287   void WriteHeader(Writer*);
288   bool WriteCode(const WasmCode*, Writer*);
289 
290   const NativeModule* const native_module_;
291   Vector<WasmCode* const> code_table_;
292   bool write_called_;
293 };
294 
NativeModuleSerializer(const NativeModule * module,Vector<WasmCode * const> code_table)295 NativeModuleSerializer::NativeModuleSerializer(
296     const NativeModule* module, Vector<WasmCode* const> code_table)
297     : native_module_(module), code_table_(code_table), write_called_(false) {
298   DCHECK_NOT_NULL(native_module_);
299   // TODO(mtrofin): persist the export wrappers. Ideally, we'd only persist
300   // the unique ones, i.e. the cache.
301 }
302 
MeasureCode(const WasmCode * code) const303 size_t NativeModuleSerializer::MeasureCode(const WasmCode* code) const {
304   if (code == nullptr) return sizeof(bool);
305   DCHECK_EQ(WasmCode::kFunction, code->kind());
306   if (FLAG_wasm_lazy_compilation && code->tier() != ExecutionTier::kTurbofan) {
307     return sizeof(bool);
308   }
309   return kCodeHeaderSize + code->instructions().size() +
310          code->reloc_info().size() + code->source_positions().size() +
311          code->protected_instructions_data().size();
312 }
313 
Measure() const314 size_t NativeModuleSerializer::Measure() const {
315   size_t size = kHeaderSize;
316   for (WasmCode* code : code_table_) {
317     size += MeasureCode(code);
318   }
319   return size;
320 }
321 
WriteHeader(Writer * writer)322 void NativeModuleSerializer::WriteHeader(Writer* writer) {
323   // TODO(eholk): We need to properly preserve the flag whether the trap
324   // handler was used or not when serializing.
325 
326   writer->Write(native_module_->num_functions());
327   writer->Write(native_module_->num_imported_functions());
328 }
329 
WriteCode(const WasmCode * code,Writer * writer)330 bool NativeModuleSerializer::WriteCode(const WasmCode* code, Writer* writer) {
331   DCHECK_IMPLIES(!FLAG_wasm_lazy_compilation, code != nullptr);
332   if (code == nullptr) {
333     writer->Write(false);
334     return true;
335   }
336   DCHECK_EQ(WasmCode::kFunction, code->kind());
337   // Only serialize TurboFan code, as Liftoff code can contain breakpoints or
338   // non-relocatable constants.
339   if (code->tier() != ExecutionTier::kTurbofan) {
340     if (FLAG_wasm_lazy_compilation) {
341       writer->Write(false);
342       return true;
343     }
344     return false;
345   }
346   writer->Write(true);
347   // Write the size of the entire code section, followed by the code header.
348   writer->Write(code->constant_pool_offset());
349   writer->Write(code->safepoint_table_offset());
350   writer->Write(code->handler_table_offset());
351   writer->Write(code->code_comments_offset());
352   writer->Write(code->unpadded_binary_size());
353   writer->Write(code->stack_slots());
354   writer->Write(code->tagged_parameter_slots());
355   writer->Write(code->instructions().length());
356   writer->Write(code->reloc_info().length());
357   writer->Write(code->source_positions().length());
358   writer->Write(code->protected_instructions_data().length());
359   writer->Write(code->kind());
360   writer->Write(code->tier());
361 
362   // Get a pointer to the destination buffer, to hold relocated code.
363   byte* serialized_code_start = writer->current_buffer().begin();
364   byte* code_start = serialized_code_start;
365   size_t code_size = code->instructions().size();
366   writer->Skip(code_size);
367   // Write the reloc info, source positions, and protected code.
368   writer->WriteVector(code->reloc_info());
369   writer->WriteVector(code->source_positions());
370   writer->WriteVector(code->protected_instructions_data());
371 #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_ARM || \
372     V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_S390X
373   // On platforms that don't support misaligned word stores, copy to an aligned
374   // buffer if necessary so we can relocate the serialized code.
375   std::unique_ptr<byte[]> aligned_buffer;
376   if (!IsAligned(reinterpret_cast<Address>(serialized_code_start),
377                  kSystemPointerSize)) {
378     // 'byte' does not guarantee an alignment but seems to work well enough in
379     // practice.
380     aligned_buffer.reset(new byte[code_size]);
381     code_start = aligned_buffer.get();
382   }
383 #endif
384   memcpy(code_start, code->instructions().begin(), code_size);
385   // Relocate the code.
386   int mask = RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
387              RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL) |
388              RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
389              RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
390              RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED);
391   RelocIterator orig_iter(code->instructions(), code->reloc_info(),
392                           code->constant_pool(), mask);
393   for (RelocIterator iter(
394            {code_start, code->instructions().size()}, code->reloc_info(),
395            reinterpret_cast<Address>(code_start) + code->constant_pool_offset(),
396            mask);
397        !iter.done(); iter.next(), orig_iter.next()) {
398     RelocInfo::Mode mode = orig_iter.rinfo()->rmode();
399     switch (mode) {
400       case RelocInfo::WASM_CALL: {
401         Address orig_target = orig_iter.rinfo()->wasm_call_address();
402         uint32_t tag =
403             native_module_->GetFunctionIndexFromJumpTableSlot(orig_target);
404         SetWasmCalleeTag(iter.rinfo(), tag);
405       } break;
406       case RelocInfo::WASM_STUB_CALL: {
407         Address target = orig_iter.rinfo()->wasm_stub_call_address();
408         uint32_t tag = native_module_->GetRuntimeStubId(target);
409         DCHECK_GT(WasmCode::kRuntimeStubCount, tag);
410         SetWasmCalleeTag(iter.rinfo(), tag);
411       } break;
412       case RelocInfo::EXTERNAL_REFERENCE: {
413         Address orig_target = orig_iter.rinfo()->target_external_reference();
414         uint32_t ext_ref_tag =
415             ExternalReferenceList::Get().tag_from_address(orig_target);
416         SetWasmCalleeTag(iter.rinfo(), ext_ref_tag);
417       } break;
418       case RelocInfo::INTERNAL_REFERENCE:
419       case RelocInfo::INTERNAL_REFERENCE_ENCODED: {
420         Address orig_target = orig_iter.rinfo()->target_internal_reference();
421         Address offset = orig_target - code->instruction_start();
422         Assembler::deserialization_set_target_internal_reference_at(
423             iter.rinfo()->pc(), offset, mode);
424       } break;
425       default:
426         UNREACHABLE();
427     }
428   }
429   // If we copied to an aligned buffer, copy code into serialized buffer.
430   if (code_start != serialized_code_start) {
431     memcpy(serialized_code_start, code_start, code_size);
432   }
433   return true;
434 }
435 
Write(Writer * writer)436 bool NativeModuleSerializer::Write(Writer* writer) {
437   DCHECK(!write_called_);
438   write_called_ = true;
439 
440   WriteHeader(writer);
441 
442   for (WasmCode* code : code_table_) {
443     if (!WriteCode(code, writer)) return false;
444   }
445   return true;
446 }
447 
WasmSerializer(NativeModule * native_module)448 WasmSerializer::WasmSerializer(NativeModule* native_module)
449     : native_module_(native_module),
450       code_table_(native_module->SnapshotCodeTable()) {}
451 
GetSerializedNativeModuleSize() const452 size_t WasmSerializer::GetSerializedNativeModuleSize() const {
453   NativeModuleSerializer serializer(native_module_, VectorOf(code_table_));
454   return kHeaderSize + serializer.Measure();
455 }
456 
SerializeNativeModule(Vector<byte> buffer) const457 bool WasmSerializer::SerializeNativeModule(Vector<byte> buffer) const {
458   NativeModuleSerializer serializer(native_module_, VectorOf(code_table_));
459   size_t measured_size = kHeaderSize + serializer.Measure();
460   if (buffer.size() < measured_size) return false;
461 
462   Writer writer(buffer);
463   WriteHeader(&writer);
464 
465   if (!serializer.Write(&writer)) return false;
466   DCHECK_EQ(measured_size, writer.bytes_written());
467   return true;
468 }
469 
470 class V8_EXPORT_PRIVATE NativeModuleDeserializer {
471  public:
472   explicit NativeModuleDeserializer(NativeModule*);
473   NativeModuleDeserializer(const NativeModuleDeserializer&) = delete;
474   NativeModuleDeserializer& operator=(const NativeModuleDeserializer&) = delete;
475 
476   bool Read(Reader* reader);
477 
478  private:
479   bool ReadHeader(Reader* reader);
480   void ReadCode(int fn_index, Reader* reader);
481 
482   NativeModule* const native_module_;
483   bool read_called_;
484 };
485 
NativeModuleDeserializer(NativeModule * native_module)486 NativeModuleDeserializer::NativeModuleDeserializer(NativeModule* native_module)
487     : native_module_(native_module), read_called_(false) {}
488 
Read(Reader * reader)489 bool NativeModuleDeserializer::Read(Reader* reader) {
490   DCHECK(!read_called_);
491   read_called_ = true;
492 
493   if (!ReadHeader(reader)) return false;
494   uint32_t total_fns = native_module_->num_functions();
495   uint32_t first_wasm_fn = native_module_->num_imported_functions();
496   WasmCodeRefScope wasm_code_ref_scope;
497   for (uint32_t i = first_wasm_fn; i < total_fns; ++i) {
498     ReadCode(i, reader);
499   }
500   return reader->current_size() == 0;
501 }
502 
ReadHeader(Reader * reader)503 bool NativeModuleDeserializer::ReadHeader(Reader* reader) {
504   size_t functions = reader->Read<uint32_t>();
505   size_t imports = reader->Read<uint32_t>();
506   return functions == native_module_->num_functions() &&
507          imports == native_module_->num_imported_functions();
508 }
509 
ReadCode(int fn_index,Reader * reader)510 void NativeModuleDeserializer::ReadCode(int fn_index, Reader* reader) {
511   bool has_code = reader->Read<bool>();
512   if (!has_code) {
513     DCHECK(FLAG_wasm_lazy_compilation ||
514            native_module_->enabled_features().has_compilation_hints());
515     native_module_->UseLazyStub(fn_index);
516     return;
517   }
518   int constant_pool_offset = reader->Read<int>();
519   int safepoint_table_offset = reader->Read<int>();
520   int handler_table_offset = reader->Read<int>();
521   int code_comment_offset = reader->Read<int>();
522   int unpadded_binary_size = reader->Read<int>();
523   int stack_slot_count = reader->Read<int>();
524   int tagged_parameter_slots = reader->Read<int>();
525   int code_size = reader->Read<int>();
526   int reloc_size = reader->Read<int>();
527   int source_position_size = reader->Read<int>();
528   int protected_instructions_size = reader->Read<int>();
529   WasmCode::Kind kind = reader->Read<WasmCode::Kind>();
530   ExecutionTier tier = reader->Read<ExecutionTier>();
531 
532   auto code_buffer = reader->ReadVector<byte>(code_size);
533   auto reloc_info = reader->ReadVector<byte>(reloc_size);
534   auto source_pos = reader->ReadVector<byte>(source_position_size);
535   auto protected_instructions =
536       reader->ReadVector<byte>(protected_instructions_size);
537 
538   CODE_SPACE_WRITE_SCOPE
539   WasmCode* code = native_module_->AddDeserializedCode(
540       fn_index, code_buffer, stack_slot_count, tagged_parameter_slots,
541       safepoint_table_offset, handler_table_offset, constant_pool_offset,
542       code_comment_offset, unpadded_binary_size, protected_instructions,
543       std::move(reloc_info), std::move(source_pos), kind, tier);
544 
545   // Relocate the code.
546   int mask = RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
547              RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL) |
548              RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
549              RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
550              RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED);
551   auto jump_tables_ref = native_module_->FindJumpTablesForRegion(
552       base::AddressRegionOf(code->instructions()));
553   for (RelocIterator iter(code->instructions(), code->reloc_info(),
554                           code->constant_pool(), mask);
555        !iter.done(); iter.next()) {
556     RelocInfo::Mode mode = iter.rinfo()->rmode();
557     switch (mode) {
558       case RelocInfo::WASM_CALL: {
559         uint32_t tag = GetWasmCalleeTag(iter.rinfo());
560         Address target =
561             native_module_->GetNearCallTargetForFunction(tag, jump_tables_ref);
562         iter.rinfo()->set_wasm_call_address(target, SKIP_ICACHE_FLUSH);
563         break;
564       }
565       case RelocInfo::WASM_STUB_CALL: {
566         uint32_t tag = GetWasmCalleeTag(iter.rinfo());
567         DCHECK_LT(tag, WasmCode::kRuntimeStubCount);
568         Address target = native_module_->GetNearRuntimeStubEntry(
569             static_cast<WasmCode::RuntimeStubId>(tag), jump_tables_ref);
570         iter.rinfo()->set_wasm_stub_call_address(target, SKIP_ICACHE_FLUSH);
571         break;
572       }
573       case RelocInfo::EXTERNAL_REFERENCE: {
574         uint32_t tag = GetWasmCalleeTag(iter.rinfo());
575         Address address = ExternalReferenceList::Get().address_from_tag(tag);
576         iter.rinfo()->set_target_external_reference(address, SKIP_ICACHE_FLUSH);
577         break;
578       }
579       case RelocInfo::INTERNAL_REFERENCE:
580       case RelocInfo::INTERNAL_REFERENCE_ENCODED: {
581         Address offset = iter.rinfo()->target_internal_reference();
582         Address target = code->instruction_start() + offset;
583         Assembler::deserialization_set_target_internal_reference_at(
584             iter.rinfo()->pc(), target, mode);
585         break;
586       }
587       default:
588         UNREACHABLE();
589     }
590   }
591 
592   code->MaybePrint();
593   code->Validate();
594 
595   // Finally, flush the icache for that code.
596   FlushInstructionCache(code->instructions().begin(),
597                         code->instructions().size());
598 }
599 
IsSupportedVersion(Vector<const byte> header)600 bool IsSupportedVersion(Vector<const byte> header) {
601   if (header.size() < WasmSerializer::kHeaderSize) return false;
602   byte current_version[WasmSerializer::kHeaderSize];
603   Writer writer({current_version, WasmSerializer::kHeaderSize});
604   WriteHeader(&writer);
605   return memcmp(header.begin(), current_version, WasmSerializer::kHeaderSize) ==
606          0;
607 }
608 
DeserializeNativeModule(Isolate * isolate,Vector<const byte> data,Vector<const byte> wire_bytes_vec,Vector<const char> source_url)609 MaybeHandle<WasmModuleObject> DeserializeNativeModule(
610     Isolate* isolate, Vector<const byte> data,
611     Vector<const byte> wire_bytes_vec, Vector<const char> source_url) {
612   if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) return {};
613   if (!IsSupportedVersion(data)) return {};
614 
615   ModuleWireBytes wire_bytes(wire_bytes_vec);
616   // TODO(titzer): module features should be part of the serialization format.
617   WasmEngine* wasm_engine = isolate->wasm_engine();
618   WasmFeatures enabled_features = WasmFeatures::FromIsolate(isolate);
619   ModuleResult decode_result = DecodeWasmModule(
620       enabled_features, wire_bytes.start(), wire_bytes.end(), false,
621       i::wasm::kWasmOrigin, isolate->counters(), isolate->metrics_recorder(),
622       isolate->GetOrRegisterRecorderContextId(isolate->native_context()),
623       DecodingMethod::kDeserialize, wasm_engine->allocator());
624   if (decode_result.failed()) return {};
625   std::shared_ptr<WasmModule> module = std::move(decode_result).value();
626   CHECK_NOT_NULL(module);
627 
628   auto shared_native_module = wasm_engine->MaybeGetNativeModule(
629       module->origin, wire_bytes_vec, isolate);
630   if (shared_native_module == nullptr) {
631     const bool kIncludeLiftoff = false;
632     size_t code_size_estimate =
633         wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get(),
634                                                             kIncludeLiftoff);
635     shared_native_module = wasm_engine->NewNativeModule(
636         isolate, enabled_features, std::move(module), code_size_estimate);
637     shared_native_module->SetWireBytes(
638         OwnedVector<uint8_t>::Of(wire_bytes_vec));
639 
640     NativeModuleDeserializer deserializer(shared_native_module.get());
641     Reader reader(data + WasmSerializer::kHeaderSize);
642     bool error = !deserializer.Read(&reader);
643     shared_native_module->compilation_state()->InitializeAfterDeserialization();
644     wasm_engine->UpdateNativeModuleCache(error, &shared_native_module, isolate);
645     if (error) return {};
646   }
647 
648   // Log the code within the generated module for profiling.
649   shared_native_module->LogWasmCodes(isolate);
650 
651   Handle<FixedArray> export_wrappers;
652   CompileJsToWasmWrappers(isolate, shared_native_module->module(),
653                           &export_wrappers);
654 
655   Handle<Script> script =
656       wasm_engine->GetOrCreateScript(isolate, shared_native_module, source_url);
657   Handle<WasmModuleObject> module_object = WasmModuleObject::New(
658       isolate, std::move(shared_native_module), script, export_wrappers);
659 
660   // Finish the Wasm script now and make it public to the debugger.
661   isolate->debug()->OnAfterCompile(script);
662   return module_object;
663 }
664 
665 }  // namespace wasm
666 }  // namespace internal
667 }  // namespace v8
668