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