1 // Copyright 2006-2008 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 <errno.h> 6 #include <signal.h> 7 #include <stdio.h> 8 9 #include "include/libplatform/libplatform.h" 10 #include "src/assembler-arch.h" 11 #include "src/base/platform/platform.h" 12 #include "src/flags.h" 13 #include "src/msan.h" 14 #include "src/snapshot/natives.h" 15 #include "src/snapshot/partial-serializer.h" 16 #include "src/snapshot/snapshot.h" 17 #include "src/snapshot/startup-serializer.h" 18 19 namespace { 20 class SnapshotWriter { 21 public: 22 SnapshotWriter() 23 : snapshot_cpp_path_(nullptr), snapshot_blob_path_(nullptr) {} 24 25 void SetEmbeddedFile(const char* embedded_cpp_file) { 26 embedded_cpp_path_ = embedded_cpp_file; 27 } 28 29 void SetEmbeddedVariant(const char* embedded_variant) { 30 embedded_variant_ = embedded_variant; 31 } 32 33 void SetSnapshotFile(const char* snapshot_cpp_file) { 34 snapshot_cpp_path_ = snapshot_cpp_file; 35 } 36 37 void SetStartupBlobFile(const char* snapshot_blob_file) { 38 snapshot_blob_path_ = snapshot_blob_file; 39 } 40 41 void WriteSnapshot(v8::StartupData blob) const { 42 // TODO(crbug/633159): if we crash before the files have been fully created, 43 // we end up with a corrupted snapshot file. The build step would succeed, 44 // but the build target is unusable. Ideally we would write out temporary 45 // files and only move them to the final destination as last step. 46 i::Vector<const i::byte> blob_vector( 47 reinterpret_cast<const i::byte*>(blob.data), blob.raw_size); 48 MaybeWriteSnapshotFile(blob_vector); 49 MaybeWriteStartupBlob(blob_vector); 50 } 51 52 void WriteEmbedded(const i::EmbeddedData* blob) const { 53 MaybeWriteEmbeddedFile(blob); 54 } 55 56 private: 57 void MaybeWriteStartupBlob(const i::Vector<const i::byte>& blob) const { 58 if (!snapshot_blob_path_) return; 59 60 FILE* fp = GetFileDescriptorOrDie(snapshot_blob_path_); 61 size_t written = fwrite(blob.begin(), 1, blob.length(), fp); 62 fclose(fp); 63 if (written != static_cast<size_t>(blob.length())) { 64 i::PrintF("Writing snapshot file failed.. Aborting.\n"); 65 remove(snapshot_blob_path_); 66 exit(1); 67 } 68 } 69 70 void MaybeWriteSnapshotFile(const i::Vector<const i::byte>& blob) const { 71 if (!snapshot_cpp_path_) return; 72 73 FILE* fp = GetFileDescriptorOrDie(snapshot_cpp_path_); 74 75 WriteSnapshotFilePrefix(fp); 76 WriteSnapshotFileData(fp, blob); 77 WriteSnapshotFileSuffix(fp); 78 79 fclose(fp); 80 } 81 82 static void WriteSnapshotFilePrefix(FILE* fp) { 83 fprintf(fp, "// Autogenerated snapshot file. Do not edit.\n\n"); 84 fprintf(fp, "#include \"src/v8.h\"\n"); 85 fprintf(fp, "#include \"src/base/platform/platform.h\"\n\n"); 86 fprintf(fp, "#include \"src/snapshot/snapshot.h\"\n\n"); 87 fprintf(fp, "namespace v8 {\n"); 88 fprintf(fp, "namespace internal {\n\n"); 89 } 90 91 static void WriteSnapshotFileSuffix(FILE* fp) { 92 fprintf(fp, "const v8::StartupData* Snapshot::DefaultSnapshotBlob() {\n"); 93 fprintf(fp, " return &blob;\n"); 94 fprintf(fp, "}\n\n"); 95 fprintf(fp, "} // namespace internal\n"); 96 fprintf(fp, "} // namespace v8\n"); 97 } 98 99 static void WriteSnapshotFileData(FILE* fp, 100 const i::Vector<const i::byte>& blob) { 101 fprintf(fp, "static const byte blob_data[] = {\n"); 102 WriteBinaryContentsAsCArray(fp, blob); 103 fprintf(fp, "};\n"); 104 fprintf(fp, "static const int blob_size = %d;\n", blob.length()); 105 fprintf(fp, "static const v8::StartupData blob =\n"); 106 fprintf(fp, "{ (const char*) blob_data, blob_size };\n"); 107 } 108 109 static void WriteBinaryContentsAsCArray( 110 FILE* fp, const i::Vector<const i::byte>& blob) { 111 for (int i = 0; i < blob.length(); i++) { 112 if ((i & 0x1F) == 0x1F) fprintf(fp, "\n"); 113 if (i > 0) fprintf(fp, ","); 114 fprintf(fp, "%u", static_cast<unsigned char>(blob.at(i))); 115 } 116 fprintf(fp, "\n"); 117 } 118 119 void MaybeWriteEmbeddedFile(const i::EmbeddedData* blob) const { 120 if (embedded_cpp_path_ == nullptr) return; 121 122 FILE* fp = GetFileDescriptorOrDie(embedded_cpp_path_); 123 124 WriteEmbeddedFilePrefix(fp); 125 WriteEmbeddedFileData(fp, blob, embedded_variant_); 126 WriteEmbeddedFileSuffix(fp, embedded_variant_); 127 128 fclose(fp); 129 } 130 131 static void WriteEmbeddedFilePrefix(FILE* fp) { 132 fprintf(fp, "// Autogenerated file. Do not edit.\n\n"); 133 fprintf(fp, "#include <cstdint>\n\n"); 134 fprintf(fp, "#include \"src/snapshot/macros.h\"\n\n"); 135 fprintf(fp, "namespace v8 {\n"); 136 fprintf(fp, "namespace internal {\n\n"); 137 fprintf(fp, "namespace {\n\n"); 138 } 139 140 static void WriteEmbeddedFileSuffix(FILE* fp, const char* embedded_variant) { 141 fprintf(fp, "} // namespace\n\n"); 142 fprintf(fp, 143 "const uint8_t* %sEmbeddedBlob() { return " 144 "v8_%s_embedded_blob_; }\n", 145 embedded_variant, embedded_variant); 146 fprintf(fp, 147 "uint32_t %sEmbeddedBlobSize() { return " 148 "v8_embedded_blob_size_; }\n\n", 149 embedded_variant); 150 fprintf(fp, "} // namespace internal\n"); 151 fprintf(fp, "} // namespace v8\n"); 152 } 153 154 static void WriteEmbeddedFileData(FILE* fp, const i::EmbeddedData* blob, 155 const char* embedded_variant) { 156 fprintf(fp, "V8_EMBEDDED_TEXT_HEADER(v8_%s_embedded_blob_)\n", 157 embedded_variant); 158 #ifdef V8_OS_MACOSX 159 // Note: On some platforms (observed on mac64), inserting labels into the 160 // .byte stream causes the compiler to reorder symbols, invalidating stored 161 // offsets. 162 // We either need to avoid doing so, or stop relying on our own offset table 163 // and directly reference symbols instead. But there is another complication 164 // there since the chrome build process on mac verifies the order of symbols 165 // present in the binary. 166 // For now, the straight-forward solution seems to be to just emit a pure 167 // .byte stream on OSX. 168 WriteBinaryContentsAsByteDirective(fp, blob->data(), blob->size()); 169 #else 170 WriteBinaryContentsAsByteDirective(fp, blob->data(), 171 i::EmbeddedData::RawDataOffset()); 172 WriteBuiltins(fp, blob, embedded_variant); 173 #endif 174 fprintf(fp, "extern \"C\" const uint8_t v8_%s_embedded_blob_[];\n", 175 embedded_variant); 176 fprintf(fp, "static const uint32_t v8_embedded_blob_size_ = %d;\n\n", 177 blob->size()); 178 } 179 180 static void WriteBuiltins(FILE* fp, const i::EmbeddedData* blob, 181 const char* embedded_variant) { 182 const bool is_default_variant = 183 std::strcmp(embedded_variant, "Default") == 0; 184 for (int i = 0; i < i::Builtins::builtin_count; i++) { 185 if (!blob->ContainsBuiltin(i)) continue; 186 187 // Labels created here will show up in backtraces. We check in 188 // Isolate::SetEmbeddedBlob that the blob layout remains unchanged, i.e. 189 // that labels do not insert bytes into the middle of the blob byte 190 // stream. 191 if (is_default_variant) { 192 // Create nicer symbol names for the default mode. 193 fprintf(fp, "__asm__(V8_ASM_LABEL(\"Builtins_%s\"));\n", 194 i::Builtins::name(i)); 195 } else { 196 fprintf(fp, "__asm__(V8_ASM_LABEL(\"%s_Builtins_%s\"));\n", 197 embedded_variant, i::Builtins::name(i)); 198 } 199 200 WriteBinaryContentsAsByteDirective( 201 fp, 202 reinterpret_cast<const uint8_t*>(blob->InstructionStartOfBuiltin(i)), 203 blob->PaddedInstructionSizeOfBuiltin(i)); 204 } 205 fprintf(fp, "\n"); 206 } 207 208 static void WriteBinaryContentsAsByteDirective(FILE* fp, const uint8_t* data, 209 uint32_t size) { 210 static const int kTextWidth = 80; 211 int current_line_length = 0; 212 int printed_chars; 213 214 fprintf(fp, "__asm__(\n"); 215 for (uint32_t i = 0; i < size; i++) { 216 if (current_line_length == 0) { 217 printed_chars = fprintf(fp, "%s", " \".byte "); 218 DCHECK_LT(0, printed_chars); 219 current_line_length += printed_chars; 220 } else { 221 printed_chars = fprintf(fp, ","); 222 DCHECK_EQ(1, printed_chars); 223 current_line_length += printed_chars; 224 } 225 226 printed_chars = fprintf(fp, "0x%02x", data[i]); 227 DCHECK_LT(0, printed_chars); 228 current_line_length += printed_chars; 229 230 if (current_line_length + strlen(",0xFF\\n\"") > kTextWidth) { 231 fprintf(fp, "\\n\"\n"); 232 current_line_length = 0; 233 } 234 } 235 236 if (current_line_length != 0) fprintf(fp, "\\n\"\n"); 237 fprintf(fp, ");\n"); 238 } 239 240 static FILE* GetFileDescriptorOrDie(const char* filename) { 241 FILE* fp = v8::base::OS::FOpen(filename, "wb"); 242 if (fp == nullptr) { 243 i::PrintF("Unable to open file \"%s\" for writing.\n", filename); 244 exit(1); 245 } 246 return fp; 247 } 248 249 const char* embedded_cpp_path_ = nullptr; 250 const char* embedded_variant_ = "Default"; 251 const char* snapshot_cpp_path_; 252 const char* snapshot_blob_path_; 253 }; 254 255 char* GetExtraCode(char* filename, const char* description) { 256 if (filename == nullptr || strlen(filename) == 0) return nullptr; 257 ::printf("Loading script for %s: %s\n", description, filename); 258 FILE* file = v8::base::OS::FOpen(filename, "rb"); 259 if (file == nullptr) { 260 fprintf(stderr, "Failed to open '%s': errno %d\n", filename, errno); 261 exit(1); 262 } 263 fseek(file, 0, SEEK_END); 264 size_t size = ftell(file); 265 rewind(file); 266 char* chars = new char[size + 1]; 267 chars[size] = '\0'; 268 for (size_t i = 0; i < size;) { 269 size_t read = fread(&chars[i], 1, size - i, file); 270 if (ferror(file)) { 271 fprintf(stderr, "Failed to read '%s': errno %d\n", filename, errno); 272 exit(1); 273 } 274 i += read; 275 } 276 fclose(file); 277 return chars; 278 } 279 280 bool RunExtraCode(v8::Isolate* isolate, v8::Local<v8::Context> context, 281 const char* utf8_source, const char* name) { 282 v8::base::ElapsedTimer timer; 283 timer.Start(); 284 v8::Context::Scope context_scope(context); 285 v8::TryCatch try_catch(isolate); 286 v8::Local<v8::String> source_string; 287 if (!v8::String::NewFromUtf8(isolate, utf8_source, v8::NewStringType::kNormal) 288 .ToLocal(&source_string)) { 289 return false; 290 } 291 v8::Local<v8::String> resource_name = 292 v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kNormal) 293 .ToLocalChecked(); 294 v8::ScriptOrigin origin(resource_name); 295 v8::ScriptCompiler::Source source(source_string, origin); 296 v8::Local<v8::Script> script; 297 if (!v8::ScriptCompiler::Compile(context, &source).ToLocal(&script)) 298 return false; 299 if (script->Run(context).IsEmpty()) return false; 300 if (i::FLAG_profile_deserialization) { 301 i::PrintF("Executing custom snapshot script %s took %0.3f ms\n", name, 302 timer.Elapsed().InMillisecondsF()); 303 } 304 timer.Stop(); 305 CHECK(!try_catch.HasCaught()); 306 return true; 307 } 308 309 v8::StartupData CreateSnapshotDataBlob(v8::SnapshotCreator* snapshot_creator, 310 const char* script_source = NULL) { 311 // Create a new isolate and a new context from scratch, optionally run 312 // a script to embed, and serialize to create a snapshot blob. 313 v8::StartupData result = {nullptr, 0}; 314 v8::base::ElapsedTimer timer; 315 timer.Start(); 316 { 317 v8::Isolate* isolate = snapshot_creator->GetIsolate(); 318 { 319 v8::HandleScope scope(isolate); 320 v8::Local<v8::Context> context = v8::Context::New(isolate); 321 if (script_source != nullptr && 322 !RunExtraCode(isolate, context, script_source, "<embedded>")) { 323 return result; 324 } 325 snapshot_creator->SetDefaultContext(context); 326 } 327 result = snapshot_creator->CreateBlob( 328 v8::SnapshotCreator::FunctionCodeHandling::kClear); 329 } 330 331 if (i::FLAG_profile_deserialization) { 332 i::PrintF("Creating snapshot took %0.3f ms\n", 333 timer.Elapsed().InMillisecondsF()); 334 } 335 timer.Stop(); 336 return result; 337 } 338 339 v8::StartupData WarmUpSnapshotDataBlob(v8::SnapshotCreator* snapshot_creator, 340 const char* warmup_source) { 341 CHECK_NOT_NULL(warmup_source); 342 // Use following steps to create a warmed up snapshot blob from a cold one: 343 // - Create a new isolate from the cold snapshot. 344 // - Create a new context to run the warmup script. This will trigger 345 // compilation of executed functions. 346 // - Create a new context. This context will be unpolluted. 347 // - Serialize the isolate and the second context into a new snapshot blob. 348 v8::StartupData result = {nullptr, 0}; 349 v8::base::ElapsedTimer timer; 350 timer.Start(); 351 { 352 v8::Isolate* isolate = snapshot_creator->GetIsolate(); 353 { 354 v8::HandleScope scope(isolate); 355 v8::Local<v8::Context> context = v8::Context::New(isolate); 356 if (!RunExtraCode(isolate, context, warmup_source, "<warm-up>")) { 357 return result; 358 } 359 } 360 { 361 v8::HandleScope handle_scope(isolate); 362 isolate->ContextDisposedNotification(false); 363 v8::Local<v8::Context> context = v8::Context::New(isolate); 364 snapshot_creator->SetDefaultContext(context); 365 } 366 result = snapshot_creator->CreateBlob( 367 v8::SnapshotCreator::FunctionCodeHandling::kKeep); 368 } 369 370 if (i::FLAG_profile_deserialization) { 371 i::PrintF("Warming up snapshot took %0.3f ms\n", 372 timer.Elapsed().InMillisecondsF()); 373 } 374 timer.Stop(); 375 return result; 376 } 377 378 void WriteEmbeddedFile(v8::SnapshotCreator* creator, SnapshotWriter* writer) { 379 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(creator->GetIsolate()); 380 isolate->PrepareEmbeddedBlobForSerialization(); 381 i::EmbeddedData embedded_blob = i::EmbeddedData::FromBlob(); 382 writer->WriteEmbedded(&embedded_blob); 383 } 384 } // namespace 385 386 int main(int argc, char** argv) { 387 v8::base::EnsureConsoleOutput(); 388 389 // Make mksnapshot runs predictable to create reproducible snapshots. 390 i::FLAG_predictable = true; 391 392 // Print the usage if an error occurs when parsing the command line 393 // flags or if the help flag is set. 394 int result = i::FlagList::SetFlagsFromCommandLine(&argc, argv, true); 395 if (result > 0 || (argc > 3) || i::FLAG_help) { 396 ::printf("Usage: %s --startup_src=... --startup_blob=... [extras]\n", 397 argv[0]); 398 i::FlagList::PrintHelp(); 399 return !i::FLAG_help; 400 } 401 402 i::CpuFeatures::Probe(true); 403 v8::V8::InitializeICUDefaultLocation(argv[0]); 404 std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform(); 405 v8::V8::InitializePlatform(platform.get()); 406 v8::V8::Initialize(); 407 408 { 409 SnapshotWriter writer; 410 if (i::FLAG_startup_src) writer.SetSnapshotFile(i::FLAG_startup_src); 411 if (i::FLAG_startup_blob) writer.SetStartupBlobFile(i::FLAG_startup_blob); 412 if (i::FLAG_embedded_builtins) { 413 if (i::FLAG_embedded_src) writer.SetEmbeddedFile(i::FLAG_embedded_src); 414 if (i::FLAG_embedded_variant) 415 writer.SetEmbeddedVariant(i::FLAG_embedded_variant); 416 } 417 418 std::unique_ptr<char> embed_script( 419 GetExtraCode(argc >= 2 ? argv[1] : nullptr, "embedding")); 420 std::unique_ptr<char> warmup_script( 421 GetExtraCode(argc >= 3 ? argv[2] : nullptr, "warm up")); 422 423 v8::StartupData blob; 424 { 425 v8::Isolate* isolate = v8::Isolate::Allocate(); 426 if (i::FLAG_embedded_builtins) { 427 // Set code range such that relative jumps for builtins to 428 // builtin calls in the snapshot are possible. 429 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 430 size_t code_range_size = 431 i::kMaximalCodeRangeSize == 0 432 ? i::kMaxPCRelativeCodeRangeInMB 433 : std::min(i::kMaximalCodeRangeSize / i::MB, 434 i::kMaxPCRelativeCodeRangeInMB); 435 i_isolate->heap()->ConfigureHeap(0, 0, code_range_size); 436 } 437 v8::SnapshotCreator snapshot_creator(isolate); 438 if (i::FLAG_embedded_builtins) { 439 // This process is a bit tricky since we might go on to make a second 440 // snapshot if a warmup script is passed. In that case, create the first 441 // snapshot without off-heap trampolines and only move code off-heap for 442 // the warmed-up snapshot. 443 if (!warmup_script) WriteEmbeddedFile(&snapshot_creator, &writer); 444 } 445 blob = CreateSnapshotDataBlob(&snapshot_creator, embed_script.get()); 446 } 447 448 if (warmup_script) { 449 CHECK(blob.raw_size > 0 && blob.data != nullptr); 450 v8::StartupData cold = blob; 451 v8::SnapshotCreator snapshot_creator(nullptr, &cold); 452 if (i::FLAG_embedded_builtins) { 453 WriteEmbeddedFile(&snapshot_creator, &writer); 454 } 455 blob = WarmUpSnapshotDataBlob(&snapshot_creator, warmup_script.get()); 456 delete[] cold.data; 457 } 458 459 CHECK(blob.data); 460 writer.WriteSnapshot(blob); 461 delete[] blob.data; 462 } 463 464 v8::V8::Dispose(); 465 v8::V8::ShutdownPlatform(); 466 return 0; 467 } 468