1 // Copyright 2016 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/snapshot/code-serializer.h" 6 7 #include <memory> 8 9 #include "src/code-stubs.h" 10 #include "src/counters.h" 11 #include "src/log.h" 12 #include "src/macro-assembler.h" 13 #include "src/objects-inl.h" 14 #include "src/snapshot/deserializer.h" 15 #include "src/snapshot/snapshot.h" 16 #include "src/version.h" 17 #include "src/wasm/wasm-module.h" 18 #include "src/wasm/wasm-objects.h" 19 20 namespace v8 { 21 namespace internal { 22 23 ScriptData* CodeSerializer::Serialize(Isolate* isolate, 24 Handle<SharedFunctionInfo> info, 25 Handle<String> source) { 26 base::ElapsedTimer timer; 27 if (FLAG_profile_deserialization) timer.Start(); 28 if (FLAG_trace_serializer) { 29 PrintF("[Serializing from"); 30 Object* script = info->script(); 31 if (script->IsScript()) Script::cast(script)->name()->ShortPrint(); 32 PrintF("]\n"); 33 } 34 35 // Serialize code object. 36 CodeSerializer cs(isolate, SerializedCodeData::SourceHash(source)); 37 DisallowHeapAllocation no_gc; 38 cs.reference_map()->AddAttachedReference(*source); 39 ScriptData* ret = cs.Serialize(info); 40 41 if (FLAG_profile_deserialization) { 42 double ms = timer.Elapsed().InMillisecondsF(); 43 int length = ret->length(); 44 PrintF("[Serializing to %d bytes took %0.3f ms]\n", length, ms); 45 } 46 47 return ret; 48 } 49 50 ScriptData* CodeSerializer::Serialize(Handle<HeapObject> obj) { 51 DisallowHeapAllocation no_gc; 52 53 VisitPointer(Handle<Object>::cast(obj).location()); 54 SerializeDeferredObjects(); 55 Pad(); 56 57 SerializedCodeData data(sink()->data(), this); 58 59 return data.GetScriptData(); 60 } 61 62 void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, 63 WhereToPoint where_to_point, int skip) { 64 if (SerializeHotObject(obj, how_to_code, where_to_point, skip)) return; 65 66 int root_index = root_index_map_.Lookup(obj); 67 if (root_index != RootIndexMap::kInvalidRootIndex) { 68 PutRoot(root_index, obj, how_to_code, where_to_point, skip); 69 return; 70 } 71 72 if (SerializeBackReference(obj, how_to_code, where_to_point, skip)) return; 73 74 FlushSkip(skip); 75 76 if (obj->IsCode()) { 77 Code* code_object = Code::cast(obj); 78 switch (code_object->kind()) { 79 case Code::OPTIMIZED_FUNCTION: // No optimized code compiled yet. 80 case Code::HANDLER: // No handlers patched in yet. 81 case Code::REGEXP: // No regexp literals initialized yet. 82 case Code::NUMBER_OF_KINDS: // Pseudo enum value. 83 case Code::BYTECODE_HANDLER: // No direct references to handlers. 84 CHECK(false); 85 case Code::BUILTIN: 86 SerializeBuiltin(code_object->builtin_index(), how_to_code, 87 where_to_point); 88 return; 89 case Code::STUB: 90 #define IC_KIND_CASE(KIND) case Code::KIND: 91 IC_KIND_LIST(IC_KIND_CASE) 92 #undef IC_KIND_CASE 93 if (code_object->builtin_index() == -1) { 94 SerializeCodeStub(code_object, how_to_code, where_to_point); 95 } else { 96 SerializeBuiltin(code_object->builtin_index(), how_to_code, 97 where_to_point); 98 } 99 return; 100 case Code::FUNCTION: 101 DCHECK(code_object->has_reloc_info_for_serialization()); 102 SerializeGeneric(code_object, how_to_code, where_to_point); 103 return; 104 default: 105 return SerializeCodeObject(code_object, how_to_code, where_to_point); 106 } 107 UNREACHABLE(); 108 } 109 110 if (ElideObject(obj)) { 111 return SerializeObject(isolate()->heap()->undefined_value(), how_to_code, 112 where_to_point, skip); 113 } 114 115 if (obj->IsScript()) { 116 // Wrapper object is a context-dependent JSValue. Reset it here. 117 Script::cast(obj)->set_wrapper(isolate()->heap()->undefined_value()); 118 } 119 120 // Past this point we should not see any (context-specific) maps anymore. 121 CHECK(!obj->IsMap()); 122 // There should be no references to the global object embedded. 123 CHECK(!obj->IsJSGlobalProxy() && !obj->IsJSGlobalObject()); 124 // There should be no hash table embedded. They would require rehashing. 125 CHECK(!obj->IsHashTable()); 126 // We expect no instantiated function objects or contexts. 127 CHECK(!obj->IsJSFunction() && !obj->IsContext()); 128 129 SerializeGeneric(obj, how_to_code, where_to_point); 130 } 131 132 void CodeSerializer::SerializeGeneric(HeapObject* heap_object, 133 HowToCode how_to_code, 134 WhereToPoint where_to_point) { 135 // Object has not yet been serialized. Serialize it here. 136 ObjectSerializer serializer(this, heap_object, &sink_, how_to_code, 137 where_to_point); 138 serializer.Serialize(); 139 } 140 141 void CodeSerializer::SerializeBuiltin(int builtin_index, HowToCode how_to_code, 142 WhereToPoint where_to_point) { 143 DCHECK((how_to_code == kPlain && where_to_point == kStartOfObject) || 144 (how_to_code == kPlain && where_to_point == kInnerPointer) || 145 (how_to_code == kFromCode && where_to_point == kInnerPointer)); 146 DCHECK_LT(builtin_index, Builtins::builtin_count); 147 DCHECK_LE(0, builtin_index); 148 149 if (FLAG_trace_serializer) { 150 PrintF(" Encoding builtin: %s\n", 151 isolate()->builtins()->name(builtin_index)); 152 } 153 154 sink_.Put(kBuiltin + how_to_code + where_to_point, "Builtin"); 155 sink_.PutInt(builtin_index, "builtin_index"); 156 } 157 158 void CodeSerializer::SerializeCodeStub(Code* code_stub, HowToCode how_to_code, 159 WhereToPoint where_to_point) { 160 // We only arrive here if we have not encountered this code stub before. 161 DCHECK(!reference_map()->Lookup(code_stub).is_valid()); 162 uint32_t stub_key = code_stub->stub_key(); 163 DCHECK(CodeStub::MajorKeyFromKey(stub_key) != CodeStub::NoCache); 164 DCHECK(!CodeStub::GetCode(isolate(), stub_key).is_null()); 165 stub_keys_.Add(stub_key); 166 167 SerializerReference reference = 168 reference_map()->AddAttachedReference(code_stub); 169 if (FLAG_trace_serializer) { 170 PrintF(" Encoding code stub %s as attached reference %d\n", 171 CodeStub::MajorName(CodeStub::MajorKeyFromKey(stub_key)), 172 reference.attached_reference_index()); 173 } 174 PutAttachedReference(reference, how_to_code, where_to_point); 175 } 176 177 MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize( 178 Isolate* isolate, ScriptData* cached_data, Handle<String> source) { 179 base::ElapsedTimer timer; 180 if (FLAG_profile_deserialization) timer.Start(); 181 182 HandleScope scope(isolate); 183 184 SerializedCodeData::SanityCheckResult sanity_check_result = 185 SerializedCodeData::CHECK_SUCCESS; 186 const SerializedCodeData scd = SerializedCodeData::FromCachedData( 187 isolate, cached_data, SerializedCodeData::SourceHash(source), 188 &sanity_check_result); 189 if (sanity_check_result != SerializedCodeData::CHECK_SUCCESS) { 190 if (FLAG_profile_deserialization) PrintF("[Cached code failed check]\n"); 191 DCHECK(cached_data->rejected()); 192 source->GetIsolate()->counters()->code_cache_reject_reason()->AddSample( 193 sanity_check_result); 194 return MaybeHandle<SharedFunctionInfo>(); 195 } 196 197 Deserializer deserializer(&scd); 198 deserializer.AddAttachedObject(source); 199 Vector<const uint32_t> code_stub_keys = scd.CodeStubKeys(); 200 for (int i = 0; i < code_stub_keys.length(); i++) { 201 deserializer.AddAttachedObject( 202 CodeStub::GetCode(isolate, code_stub_keys[i]).ToHandleChecked()); 203 } 204 205 // Deserialize. 206 Handle<HeapObject> as_heap_object; 207 if (!deserializer.DeserializeObject(isolate).ToHandle(&as_heap_object)) { 208 // Deserializing may fail if the reservations cannot be fulfilled. 209 if (FLAG_profile_deserialization) PrintF("[Deserializing failed]\n"); 210 return MaybeHandle<SharedFunctionInfo>(); 211 } 212 213 Handle<SharedFunctionInfo> result = 214 Handle<SharedFunctionInfo>::cast(as_heap_object); 215 if (FLAG_profile_deserialization) { 216 double ms = timer.Elapsed().InMillisecondsF(); 217 int length = cached_data->length(); 218 PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms); 219 } 220 result->set_deserialized(true); 221 222 if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) { 223 String* name = isolate->heap()->empty_string(); 224 if (result->script()->IsScript()) { 225 Script* script = Script::cast(result->script()); 226 if (script->name()->IsString()) name = String::cast(script->name()); 227 } 228 PROFILE(isolate, CodeCreateEvent(CodeEventListener::SCRIPT_TAG, 229 result->abstract_code(), *result, name)); 230 } 231 return scope.CloseAndEscape(result); 232 } 233 234 WasmCompiledModuleSerializer::WasmCompiledModuleSerializer( 235 Isolate* isolate, uint32_t source_hash, Handle<Context> native_context, 236 Handle<SeqOneByteString> module_bytes) 237 : CodeSerializer(isolate, source_hash) { 238 reference_map()->AddAttachedReference(*isolate->native_context()); 239 reference_map()->AddAttachedReference(*module_bytes); 240 } 241 242 std::unique_ptr<ScriptData> WasmCompiledModuleSerializer::SerializeWasmModule( 243 Isolate* isolate, Handle<FixedArray> input) { 244 Handle<WasmCompiledModule> compiled_module = 245 Handle<WasmCompiledModule>::cast(input); 246 WasmCompiledModuleSerializer wasm_cs(isolate, 0, isolate->native_context(), 247 handle(compiled_module->module_bytes())); 248 ScriptData* data = wasm_cs.Serialize(compiled_module); 249 return std::unique_ptr<ScriptData>(data); 250 } 251 252 MaybeHandle<FixedArray> WasmCompiledModuleSerializer::DeserializeWasmModule( 253 Isolate* isolate, ScriptData* data, Vector<const byte> wire_bytes) { 254 MaybeHandle<FixedArray> nothing; 255 if (!wasm::IsWasmCodegenAllowed(isolate, isolate->native_context())) { 256 return nothing; 257 } 258 SerializedCodeData::SanityCheckResult sanity_check_result = 259 SerializedCodeData::CHECK_SUCCESS; 260 261 const SerializedCodeData scd = SerializedCodeData::FromCachedData( 262 isolate, data, 0, &sanity_check_result); 263 264 if (sanity_check_result != SerializedCodeData::CHECK_SUCCESS) { 265 return nothing; 266 } 267 268 Deserializer deserializer(&scd, true); 269 deserializer.AddAttachedObject(isolate->native_context()); 270 271 MaybeHandle<String> maybe_wire_bytes_as_string = 272 isolate->factory()->NewStringFromOneByte(wire_bytes, TENURED); 273 Handle<String> wire_bytes_as_string; 274 if (!maybe_wire_bytes_as_string.ToHandle(&wire_bytes_as_string)) { 275 return nothing; 276 } 277 deserializer.AddAttachedObject( 278 handle(SeqOneByteString::cast(*wire_bytes_as_string))); 279 280 Vector<const uint32_t> stub_keys = scd.CodeStubKeys(); 281 for (int i = 0; i < stub_keys.length(); ++i) { 282 deserializer.AddAttachedObject( 283 CodeStub::GetCode(isolate, stub_keys[i]).ToHandleChecked()); 284 } 285 286 MaybeHandle<HeapObject> obj = deserializer.DeserializeObject(isolate); 287 if (obj.is_null() || !obj.ToHandleChecked()->IsFixedArray()) return nothing; 288 // Cast without type checks, as the module wrapper is not there yet. 289 Handle<WasmCompiledModule> compiled_module( 290 static_cast<WasmCompiledModule*>(*obj.ToHandleChecked()), isolate); 291 292 WasmCompiledModule::ReinitializeAfterDeserialization(isolate, 293 compiled_module); 294 DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module)); 295 return compiled_module; 296 } 297 298 void WasmCompiledModuleSerializer::SerializeCodeObject( 299 Code* code_object, HowToCode how_to_code, WhereToPoint where_to_point) { 300 Code::Kind kind = code_object->kind(); 301 switch (kind) { 302 case Code::WASM_FUNCTION: 303 case Code::JS_TO_WASM_FUNCTION: 304 // Just serialize the code_object. 305 break; 306 case Code::WASM_TO_JS_FUNCTION: 307 // Serialize the illegal builtin instead. On instantiation of a 308 // deserialized module, these will be replaced again. 309 code_object = *isolate()->builtins()->Illegal(); 310 break; 311 default: 312 UNREACHABLE(); 313 } 314 SerializeGeneric(code_object, how_to_code, where_to_point); 315 } 316 317 bool WasmCompiledModuleSerializer::ElideObject(Object* obj) { 318 return obj->IsWeakCell() || obj->IsForeign() || obj->IsBreakPointInfo(); 319 } 320 321 class Checksum { 322 public: 323 explicit Checksum(Vector<const byte> payload) { 324 #ifdef MEMORY_SANITIZER 325 // Computing the checksum includes padding bytes for objects like strings. 326 // Mark every object as initialized in the code serializer. 327 MSAN_MEMORY_IS_INITIALIZED(payload.start(), payload.length()); 328 #endif // MEMORY_SANITIZER 329 // Fletcher's checksum. Modified to reduce 64-bit sums to 32-bit. 330 uintptr_t a = 1; 331 uintptr_t b = 0; 332 const uintptr_t* cur = reinterpret_cast<const uintptr_t*>(payload.start()); 333 DCHECK(IsAligned(payload.length(), kIntptrSize)); 334 const uintptr_t* end = cur + payload.length() / kIntptrSize; 335 while (cur < end) { 336 // Unsigned overflow expected and intended. 337 a += *cur++; 338 b += a; 339 } 340 #if V8_HOST_ARCH_64_BIT 341 a ^= a >> 32; 342 b ^= b >> 32; 343 #endif // V8_HOST_ARCH_64_BIT 344 a_ = static_cast<uint32_t>(a); 345 b_ = static_cast<uint32_t>(b); 346 } 347 348 bool Check(uint32_t a, uint32_t b) const { return a == a_ && b == b_; } 349 350 uint32_t a() const { return a_; } 351 uint32_t b() const { return b_; } 352 353 private: 354 uint32_t a_; 355 uint32_t b_; 356 357 DISALLOW_COPY_AND_ASSIGN(Checksum); 358 }; 359 360 SerializedCodeData::SerializedCodeData(const List<byte>* payload, 361 const CodeSerializer* cs) { 362 DisallowHeapAllocation no_gc; 363 const List<uint32_t>* stub_keys = cs->stub_keys(); 364 365 List<Reservation> reservations; 366 cs->EncodeReservations(&reservations); 367 368 // Calculate sizes. 369 int reservation_size = reservations.length() * kInt32Size; 370 int num_stub_keys = stub_keys->length(); 371 int stub_keys_size = stub_keys->length() * kInt32Size; 372 int payload_offset = kHeaderSize + reservation_size + stub_keys_size; 373 int padded_payload_offset = POINTER_SIZE_ALIGN(payload_offset); 374 int size = padded_payload_offset + payload->length(); 375 376 // Allocate backing store and create result data. 377 AllocateData(size); 378 379 // Set header values. 380 SetMagicNumber(cs->isolate()); 381 SetHeaderValue(kVersionHashOffset, Version::Hash()); 382 SetHeaderValue(kSourceHashOffset, cs->source_hash()); 383 SetHeaderValue(kCpuFeaturesOffset, 384 static_cast<uint32_t>(CpuFeatures::SupportedFeatures())); 385 SetHeaderValue(kFlagHashOffset, FlagList::Hash()); 386 SetHeaderValue(kNumReservationsOffset, reservations.length()); 387 SetHeaderValue(kNumCodeStubKeysOffset, num_stub_keys); 388 SetHeaderValue(kPayloadLengthOffset, payload->length()); 389 390 // Copy reservation chunk sizes. 391 CopyBytes(data_ + kHeaderSize, reinterpret_cast<byte*>(reservations.begin()), 392 reservation_size); 393 394 // Copy code stub keys. 395 CopyBytes(data_ + kHeaderSize + reservation_size, 396 reinterpret_cast<byte*>(stub_keys->begin()), stub_keys_size); 397 398 memset(data_ + payload_offset, 0, padded_payload_offset - payload_offset); 399 400 // Copy serialized data. 401 CopyBytes(data_ + padded_payload_offset, payload->begin(), 402 static_cast<size_t>(payload->length())); 403 404 Checksum checksum(DataWithoutHeader()); 405 SetHeaderValue(kChecksum1Offset, checksum.a()); 406 SetHeaderValue(kChecksum2Offset, checksum.b()); 407 } 408 409 SerializedCodeData::SanityCheckResult SerializedCodeData::SanityCheck( 410 Isolate* isolate, uint32_t expected_source_hash) const { 411 if (this->size_ < kHeaderSize) return INVALID_HEADER; 412 uint32_t magic_number = GetMagicNumber(); 413 if (magic_number != ComputeMagicNumber(isolate)) return MAGIC_NUMBER_MISMATCH; 414 uint32_t version_hash = GetHeaderValue(kVersionHashOffset); 415 uint32_t source_hash = GetHeaderValue(kSourceHashOffset); 416 uint32_t cpu_features = GetHeaderValue(kCpuFeaturesOffset); 417 uint32_t flags_hash = GetHeaderValue(kFlagHashOffset); 418 uint32_t c1 = GetHeaderValue(kChecksum1Offset); 419 uint32_t c2 = GetHeaderValue(kChecksum2Offset); 420 if (version_hash != Version::Hash()) return VERSION_MISMATCH; 421 if (source_hash != expected_source_hash) return SOURCE_MISMATCH; 422 if (cpu_features != static_cast<uint32_t>(CpuFeatures::SupportedFeatures())) { 423 return CPU_FEATURES_MISMATCH; 424 } 425 if (flags_hash != FlagList::Hash()) return FLAGS_MISMATCH; 426 if (!Checksum(DataWithoutHeader()).Check(c1, c2)) return CHECKSUM_MISMATCH; 427 return CHECK_SUCCESS; 428 } 429 430 uint32_t SerializedCodeData::SourceHash(Handle<String> source) { 431 return source->length(); 432 } 433 434 // Return ScriptData object and relinquish ownership over it to the caller. 435 ScriptData* SerializedCodeData::GetScriptData() { 436 DCHECK(owns_data_); 437 ScriptData* result = new ScriptData(data_, size_); 438 result->AcquireDataOwnership(); 439 owns_data_ = false; 440 data_ = NULL; 441 return result; 442 } 443 444 Vector<const SerializedData::Reservation> SerializedCodeData::Reservations() 445 const { 446 return Vector<const Reservation>( 447 reinterpret_cast<const Reservation*>(data_ + kHeaderSize), 448 GetHeaderValue(kNumReservationsOffset)); 449 } 450 451 Vector<const byte> SerializedCodeData::Payload() const { 452 int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size; 453 int code_stubs_size = GetHeaderValue(kNumCodeStubKeysOffset) * kInt32Size; 454 int payload_offset = kHeaderSize + reservations_size + code_stubs_size; 455 int padded_payload_offset = POINTER_SIZE_ALIGN(payload_offset); 456 const byte* payload = data_ + padded_payload_offset; 457 DCHECK(IsAligned(reinterpret_cast<intptr_t>(payload), kPointerAlignment)); 458 int length = GetHeaderValue(kPayloadLengthOffset); 459 DCHECK_EQ(data_ + size_, payload + length); 460 return Vector<const byte>(payload, length); 461 } 462 463 Vector<const uint32_t> SerializedCodeData::CodeStubKeys() const { 464 int reservations_size = GetHeaderValue(kNumReservationsOffset) * kInt32Size; 465 const byte* start = data_ + kHeaderSize + reservations_size; 466 return Vector<const uint32_t>(reinterpret_cast<const uint32_t*>(start), 467 GetHeaderValue(kNumCodeStubKeysOffset)); 468 } 469 470 SerializedCodeData::SerializedCodeData(ScriptData* data) 471 : SerializedData(const_cast<byte*>(data->data()), data->length()) {} 472 473 const SerializedCodeData SerializedCodeData::FromCachedData( 474 Isolate* isolate, ScriptData* cached_data, uint32_t expected_source_hash, 475 SanityCheckResult* rejection_result) { 476 DisallowHeapAllocation no_gc; 477 SerializedCodeData scd(cached_data); 478 *rejection_result = scd.SanityCheck(isolate, expected_source_hash); 479 if (*rejection_result != CHECK_SUCCESS) { 480 cached_data->Reject(); 481 return SerializedCodeData(nullptr, 0); 482 } 483 return scd; 484 } 485 486 } // namespace internal 487 } // namespace v8 488