blob: f7efe476a1aa00f10d4aa25a6975dc391a3eb611 [file] [log] [blame]
oth05c26fde2015-04-05 14:30:571// Copyright 2013 The Chromium 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 "gin/v8_initializer.h"
6
7#include "base/basictypes.h"
8#include "base/files/file.h"
9#include "base/files/file_path.h"
10#include "base/files/memory_mapped_file.h"
11#include "base/logging.h"
agrievefd2d44ab2015-06-19 04:33:0312#include "base/memory/scoped_ptr.h"
oth575f7fb52015-05-08 17:35:0013#include "base/metrics/histogram.h"
oth05c26fde2015-04-05 14:30:5714#include "base/rand_util.h"
15#include "base/strings/sys_string_conversions.h"
oth575f7fb52015-05-08 17:35:0016#include "base/threading/platform_thread.h"
17#include "base/time/time.h"
oth05c26fde2015-04-05 14:30:5718#include "crypto/sha2.h"
19
20#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
21#if defined(OS_MACOSX)
22#include "base/mac/foundation_util.h"
23#endif // OS_MACOSX
24#include "base/path_service.h"
25#endif // V8_USE_EXTERNAL_STARTUP_DATA
26
27namespace gin {
28
29namespace {
30
agrievefd2d44ab2015-06-19 04:33:0331// None of these globals are ever freed nor closed.
oth05c26fde2015-04-05 14:30:5732base::MemoryMappedFile* g_mapped_natives = nullptr;
33base::MemoryMappedFile* g_mapped_snapshot = nullptr;
34
35#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
agrievefd2d44ab2015-06-19 04:33:0336
37const base::PlatformFile kInvalidPlatformFile =
38#if defined(OS_WIN)
39 INVALID_HANDLE_VALUE;
40#else
41 -1;
42#endif
43
44// File handles intentionally never closed. Not using File here because its
45// Windows implementation guards against two instances owning the same
46// PlatformFile (which we allow since we know it is never freed).
47base::PlatformFile g_natives_pf = kInvalidPlatformFile;
48base::PlatformFile g_snapshot_pf = kInvalidPlatformFile;
49base::MemoryMappedFile::Region g_natives_region;
50base::MemoryMappedFile::Region g_snapshot_region;
51
rmcilroy54fab5e2015-04-06 21:14:5852#if !defined(OS_MACOSX)
53const int kV8SnapshotBasePathKey =
54#if defined(OS_ANDROID)
55 base::DIR_ANDROID_APP_DATA;
56#elif defined(OS_POSIX)
57 base::DIR_EXE;
58#elif defined(OS_WIN)
59 base::DIR_MODULE;
60#endif // OS_ANDROID
61#endif // !OS_MACOSX
62
63const char kNativesFileName[] = "natives_blob.bin";
64const char kSnapshotFileName[] = "snapshot_blob.bin";
65
oth575f7fb52015-05-08 17:35:0066// Constants for snapshot loading retries taken from:
67// https://support.microsoft.com/en-us/kb/316609.
68const int kMaxOpenAttempts = 5;
69const int kOpenRetryDelayMillis = 250;
70
erikcorryc94eff12015-06-08 11:29:1671void GetV8FilePath(const char* file_name, base::FilePath* path_out) {
rmcilroy54fab5e2015-04-06 21:14:5872#if !defined(OS_MACOSX)
73 base::FilePath data_path;
74 PathService::Get(kV8SnapshotBasePathKey, &data_path);
75 DCHECK(!data_path.empty());
76
erikcorryc94eff12015-06-08 11:29:1677 *path_out = data_path.AppendASCII(file_name);
rmcilroy54fab5e2015-04-06 21:14:5878#else // !defined(OS_MACOSX)
79 base::ScopedCFTypeRef<CFStringRef> natives_file_name(
erikcorryc94eff12015-06-08 11:29:1680 base::SysUTF8ToCFStringRef(file_name));
81 *path_out = base::mac::PathForFrameworkBundleResource(natives_file_name);
rmcilroy54fab5e2015-04-06 21:14:5882#endif // !defined(OS_MACOSX)
erikcorryc94eff12015-06-08 11:29:1683 DCHECK(!path_out->empty());
rmcilroy54fab5e2015-04-06 21:14:5884}
85
agrievefd2d44ab2015-06-19 04:33:0386static bool MapV8File(base::PlatformFile platform_file,
erikcorryc94eff12015-06-08 11:29:1687 base::MemoryMappedFile::Region region,
88 base::MemoryMappedFile** mmapped_file_out) {
89 DCHECK(*mmapped_file_out == NULL);
agrievefd2d44ab2015-06-19 04:33:0390 scoped_ptr<base::MemoryMappedFile> mmapped_file(new base::MemoryMappedFile());
91 if (mmapped_file->Initialize(base::File(platform_file), region)) {
92 *mmapped_file_out = mmapped_file.release();
93 return true;
oth05c26fde2015-04-05 14:30:5794 }
agrievefd2d44ab2015-06-19 04:33:0395 return false;
oth05c26fde2015-04-05 14:30:5796}
97
agrievefd2d44ab2015-06-19 04:33:0398base::PlatformFile OpenV8File(const char* file_name,
99 base::MemoryMappedFile::Region* region_out) {
oth575f7fb52015-05-08 17:35:00100 // Re-try logic here is motivated by http://crbug.com/479537
101 // for A/V on Windows (https://support.microsoft.com/en-us/kb/316609).
102
103 // These match tools/metrics/histograms.xml
104 enum OpenV8FileResult {
105 OPENED = 0,
106 OPENED_RETRY,
107 FAILED_IN_USE,
108 FAILED_OTHER,
109 MAX_VALUE
110 };
111
agrievefd2d44ab2015-06-19 04:33:03112 base::FilePath path;
113 GetV8FilePath(file_name, &path);
114
oth575f7fb52015-05-08 17:35:00115 OpenV8FileResult result = OpenV8FileResult::FAILED_IN_USE;
agrievefd2d44ab2015-06-19 04:33:03116 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
117 base::File file;
oth575f7fb52015-05-08 17:35:00118 for (int attempt = 0; attempt < kMaxOpenAttempts; attempt++) {
119 file.Initialize(path, flags);
120 if (file.IsValid()) {
agrievefd2d44ab2015-06-19 04:33:03121 *region_out = base::MemoryMappedFile::Region::kWholeFile;
oth575f7fb52015-05-08 17:35:00122 if (attempt == 0) {
123 result = OpenV8FileResult::OPENED;
124 break;
125 } else {
126 result = OpenV8FileResult::OPENED_RETRY;
127 break;
128 }
129 } else if (file.error_details() != base::File::FILE_ERROR_IN_USE) {
130 result = OpenV8FileResult::FAILED_OTHER;
131 break;
132 } else if (kMaxOpenAttempts - 1 != attempt) {
133 base::PlatformThread::Sleep(
134 base::TimeDelta::FromMilliseconds(kOpenRetryDelayMillis));
135 }
136 }
137
138 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.OpenV8File.Result",
139 result,
140 OpenV8FileResult::MAX_VALUE);
agrievefd2d44ab2015-06-19 04:33:03141 return file.TakePlatformFile();
142}
oth575f7fb52015-05-08 17:35:00143
agrievefd2d44ab2015-06-19 04:33:03144void OpenNativesFileIfNecessary() {
145 if (g_natives_pf == kInvalidPlatformFile) {
146 g_natives_pf = OpenV8File(kNativesFileName, &g_natives_region);
147 }
148}
149
150void OpenSnapshotFileIfNecessary() {
151 if (g_snapshot_pf == kInvalidPlatformFile) {
152 g_snapshot_pf = OpenV8File(kSnapshotFileName, &g_snapshot_region);
153 }
oth575f7fb52015-05-08 17:35:00154}
155
oth05c26fde2015-04-05 14:30:57156#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
erikcorryc94eff12015-06-08 11:29:16157bool VerifyV8StartupFile(base::MemoryMappedFile** file,
158 const unsigned char* fingerprint) {
oth05c26fde2015-04-05 14:30:57159 unsigned char output[crypto::kSHA256Length];
160 crypto::SHA256HashString(
erikcorryc94eff12015-06-08 11:29:16161 base::StringPiece(reinterpret_cast<const char*>((*file)->data()),
162 (*file)->length()),
oth05c26fde2015-04-05 14:30:57163 output, sizeof(output));
erikcorryc94eff12015-06-08 11:29:16164 if (!memcmp(fingerprint, output, sizeof(output))) {
165 return true;
166 }
167 delete *file;
168 *file = NULL;
169 return false;
oth05c26fde2015-04-05 14:30:57170}
171#endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
172#endif // V8_USE_EXTERNAL_STARTUP_DATA
173
rmcilroy54fab5e2015-04-06 21:14:58174bool GenerateEntropy(unsigned char* buffer, size_t amount) {
175 base::RandBytes(buffer, amount);
176 return true;
177}
178
oth05c26fde2015-04-05 14:30:57179} // namespace
180
181#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
oth05c26fde2015-04-05 14:30:57182#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
183// Defined in gen/gin/v8_snapshot_fingerprint.cc
184extern const unsigned char g_natives_fingerprint[];
185extern const unsigned char g_snapshot_fingerprint[];
186#endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
187
erikcorryc94eff12015-06-08 11:29:16188enum LoadV8FileResult {
189 V8_LOAD_SUCCESS = 0,
190 V8_LOAD_FAILED_OPEN,
191 V8_LOAD_FAILED_MAP,
192 V8_LOAD_FAILED_VERIFY,
193 V8_LOAD_MAX_VALUE
194};
oth575f7fb52015-05-08 17:35:00195
agrievefd2d44ab2015-06-19 04:33:03196static LoadV8FileResult MapVerify(base::PlatformFile platform_file,
197 const base::MemoryMappedFile::Region& region,
erikcorryc94eff12015-06-08 11:29:16198#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
agrievefd2d44ab2015-06-19 04:33:03199 const unsigned char* fingerprint,
erikcorryc94eff12015-06-08 11:29:16200#endif
agrievefd2d44ab2015-06-19 04:33:03201 base::MemoryMappedFile** mmapped_file_out) {
202 if (platform_file == kInvalidPlatformFile)
erikcorryc94eff12015-06-08 11:29:16203 return V8_LOAD_FAILED_OPEN;
agrievefd2d44ab2015-06-19 04:33:03204 if (!MapV8File(platform_file, region, mmapped_file_out))
erikcorryc94eff12015-06-08 11:29:16205 return V8_LOAD_FAILED_MAP;
oth05c26fde2015-04-05 14:30:57206#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
erikcorryc94eff12015-06-08 11:29:16207 if (!VerifyV8StartupFile(mmapped_file_out, fingerprint))
208 return V8_LOAD_FAILED_VERIFY;
oth05c26fde2015-04-05 14:30:57209#endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
erikcorryc94eff12015-06-08 11:29:16210 return V8_LOAD_SUCCESS;
oth05c26fde2015-04-05 14:30:57211}
212
213// static
erikcorryc94eff12015-06-08 11:29:16214void V8Initializer::LoadV8Snapshot() {
215 if (g_mapped_snapshot)
216 return;
217
agrievefd2d44ab2015-06-19 04:33:03218 OpenSnapshotFileIfNecessary();
219 LoadV8FileResult result = MapVerify(g_snapshot_pf, g_snapshot_region,
erikcorryc94eff12015-06-08 11:29:16220#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
agrievefd2d44ab2015-06-19 04:33:03221 g_snapshot_fingerprint,
erikcorryc94eff12015-06-08 11:29:16222#endif
agrievefd2d44ab2015-06-19 04:33:03223 &g_mapped_snapshot);
224 // V8 can't start up without the source of the natives, but it can
225 // start up (slower) without the snapshot.
erikcorryc94eff12015-06-08 11:29:16226 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.LoadV8Snapshot.Result", result,
227 V8_LOAD_MAX_VALUE);
228}
229
230void V8Initializer::LoadV8Natives() {
231 if (g_mapped_natives)
232 return;
233
agrievefd2d44ab2015-06-19 04:33:03234 OpenNativesFileIfNecessary();
235 LoadV8FileResult result = MapVerify(g_natives_pf, g_natives_region,
erikcorryc94eff12015-06-08 11:29:16236#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
agrievefd2d44ab2015-06-19 04:33:03237 g_natives_fingerprint,
erikcorryc94eff12015-06-08 11:29:16238#endif
agrievefd2d44ab2015-06-19 04:33:03239 &g_mapped_natives);
erikcorryc94eff12015-06-08 11:29:16240 if (result != V8_LOAD_SUCCESS) {
241 LOG(FATAL) << "Couldn't mmap v8 natives data file, status code is "
242 << static_cast<int>(result);
243 }
244}
245
246// static
247void V8Initializer::LoadV8SnapshotFromFD(base::PlatformFile snapshot_pf,
oth05c26fde2015-04-05 14:30:57248 int64 snapshot_offset,
249 int64 snapshot_size) {
erikcorryc94eff12015-06-08 11:29:16250 if (g_mapped_snapshot)
251 return;
252
agrievefd2d44ab2015-06-19 04:33:03253 if (snapshot_pf == kInvalidPlatformFile)
erikcorryc94eff12015-06-08 11:29:16254 return;
255
256 base::MemoryMappedFile::Region snapshot_region =
257 base::MemoryMappedFile::Region::kWholeFile;
258 if (snapshot_size != 0 || snapshot_offset != 0) {
agrievefd2d44ab2015-06-19 04:33:03259 snapshot_region.offset = snapshot_offset;
260 snapshot_region.size = snapshot_size;
erikcorryc94eff12015-06-08 11:29:16261 }
262
263 LoadV8FileResult result = V8_LOAD_SUCCESS;
agrievefd2d44ab2015-06-19 04:33:03264 if (!MapV8File(snapshot_pf, snapshot_region, &g_mapped_snapshot))
erikcorryc94eff12015-06-08 11:29:16265 result = V8_LOAD_FAILED_MAP;
266#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
267 if (!VerifyV8StartupFile(&g_mapped_snapshot, g_snapshot_fingerprint))
268 result = V8_LOAD_FAILED_VERIFY;
269#endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
270 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.LoadV8Snapshot.Result", result,
271 V8_LOAD_MAX_VALUE);
272}
273
274// static
275void V8Initializer::LoadV8NativesFromFD(base::PlatformFile natives_pf,
276 int64 natives_offset,
277 int64 natives_size) {
278 if (g_mapped_natives)
279 return;
280
agrievefd2d44ab2015-06-19 04:33:03281 CHECK_NE(natives_pf, kInvalidPlatformFile);
oth05c26fde2015-04-05 14:30:57282
283 base::MemoryMappedFile::Region natives_region =
284 base::MemoryMappedFile::Region::kWholeFile;
285 if (natives_size != 0 || natives_offset != 0) {
agrievefd2d44ab2015-06-19 04:33:03286 natives_region.offset = natives_offset;
287 natives_region.size = natives_size;
oth05c26fde2015-04-05 14:30:57288 }
289
agrievefd2d44ab2015-06-19 04:33:03290 if (!MapV8File(natives_pf, natives_region, &g_mapped_natives)) {
erikcorryc94eff12015-06-08 11:29:16291 LOG(FATAL) << "Couldn't mmap v8 natives data file";
oth05c26fde2015-04-05 14:30:57292 }
erikcorryc94eff12015-06-08 11:29:16293#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
294 if (!VerifyV8StartupFile(&g_mapped_natives, g_natives_fingerprint)) {
295 LOG(FATAL) << "Couldn't verify contents of v8 natives data file";
296 }
297#endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
oth05c26fde2015-04-05 14:30:57298}
299
rmcilroy54fab5e2015-04-06 21:14:58300// static
agrievefd2d44ab2015-06-19 04:33:03301base::PlatformFile V8Initializer::GetOpenNativesFileForChildProcesses(
302 base::MemoryMappedFile::Region* region_out) {
303 OpenNativesFileIfNecessary();
304 *region_out = g_natives_region;
305 return g_natives_pf;
rmcilroy54fab5e2015-04-06 21:14:58306}
307
agrievefd2d44ab2015-06-19 04:33:03308// static
309base::PlatformFile V8Initializer::GetOpenSnapshotFileForChildProcesses(
310 base::MemoryMappedFile::Region* region_out) {
311 OpenSnapshotFileIfNecessary();
312 *region_out = g_snapshot_region;
313 return g_snapshot_pf;
314}
315#endif // defined(V8_USE_EXTERNAL_STARTUP_DATA)
oth05c26fde2015-04-05 14:30:57316
317// static
jochena0b121b2015-04-30 12:56:27318void V8Initializer::Initialize(gin::IsolateHolder::ScriptMode mode) {
oth05c26fde2015-04-05 14:30:57319 static bool v8_is_initialized = false;
320 if (v8_is_initialized)
321 return;
322
323 v8::V8::InitializePlatform(V8Platform::Get());
oth05c26fde2015-04-05 14:30:57324
325 if (gin::IsolateHolder::kStrictMode == mode) {
326 static const char use_strict[] = "--use_strict";
327 v8::V8::SetFlagsFromString(use_strict, sizeof(use_strict) - 1);
328 }
329
330#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
331 v8::StartupData natives;
332 natives.data = reinterpret_cast<const char*>(g_mapped_natives->data());
333 natives.raw_size = static_cast<int>(g_mapped_natives->length());
334 v8::V8::SetNativesDataBlob(&natives);
335
erikcorryc94eff12015-06-08 11:29:16336 if (g_mapped_snapshot != NULL) {
337 v8::StartupData snapshot;
338 snapshot.data = reinterpret_cast<const char*>(g_mapped_snapshot->data());
339 snapshot.raw_size = static_cast<int>(g_mapped_snapshot->length());
340 v8::V8::SetSnapshotDataBlob(&snapshot);
341 }
oth05c26fde2015-04-05 14:30:57342#endif // V8_USE_EXTERNAL_STARTUP_DATA
343
344 v8::V8::SetEntropySource(&GenerateEntropy);
345 v8::V8::Initialize();
346
347 v8_is_initialized = true;
348}
349
350// static
351void V8Initializer::GetV8ExternalSnapshotData(const char** natives_data_out,
352 int* natives_size_out,
353 const char** snapshot_data_out,
354 int* snapshot_size_out) {
erikcorryc94eff12015-06-08 11:29:16355 if (g_mapped_natives) {
356 *natives_data_out = reinterpret_cast<const char*>(g_mapped_natives->data());
357 *natives_size_out = static_cast<int>(g_mapped_natives->length());
358 } else {
359 *natives_data_out = NULL;
360 *natives_size_out = 0;
oth05c26fde2015-04-05 14:30:57361 }
erikcorryc94eff12015-06-08 11:29:16362 if (g_mapped_snapshot) {
363 *snapshot_data_out =
364 reinterpret_cast<const char*>(g_mapped_snapshot->data());
365 *snapshot_size_out = static_cast<int>(g_mapped_snapshot->length());
366 } else {
367 *snapshot_data_out = NULL;
368 *snapshot_size_out = 0;
369 }
oth05c26fde2015-04-05 14:30:57370}
371
372} // namespace gin