1 // 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.
5 #include "gin/v8_initializer.h"
7 #include "base/basictypes.h"
8 #include "base/debug/alias.h"
9 #include "base/files/file.h"
10 #include "base/files/file_path.h"
11 #include "base/files/memory_mapped_file.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/metrics/histogram.h"
15 #include "base/rand_util.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/threading/platform_thread.h"
18 #include "base/time/time.h"
19 #include "crypto/sha2.h"
21 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
22 #if defined(OS_ANDROID)
23 #include "base/android/apk_assets.h"
25 #if defined(OS_MACOSX)
26 #include "base/mac/foundation_util.h"
28 #include "base/path_service.h"
29 #endif // V8_USE_EXTERNAL_STARTUP_DATA
35 // None of these globals are ever freed nor closed.
36 base::MemoryMappedFile
* g_mapped_natives
= nullptr;
37 base::MemoryMappedFile
* g_mapped_snapshot
= nullptr;
39 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
41 const base::PlatformFile kInvalidPlatformFile
=
48 // File handles intentionally never closed. Not using File here because its
49 // Windows implementation guards against two instances owning the same
50 // PlatformFile (which we allow since we know it is never freed).
51 base::PlatformFile g_natives_pf
= kInvalidPlatformFile
;
52 base::PlatformFile g_snapshot_pf
= kInvalidPlatformFile
;
53 base::MemoryMappedFile::Region g_natives_region
;
54 base::MemoryMappedFile::Region g_snapshot_region
;
56 const char kNativesFileName
[] = "natives_blob.bin";
57 const char kSnapshotFileName
[] = "snapshot_blob.bin";
59 void GetV8FilePath(const char* file_name
, base::FilePath
* path_out
) {
60 #if !defined(OS_MACOSX)
61 base::FilePath data_path
;
62 #if defined(OS_ANDROID)
63 // This is the path within the .apk.
64 data_path
= base::FilePath(FILE_PATH_LITERAL("assets"));
65 #elif defined(OS_POSIX)
66 PathService::Get(base::DIR_EXE
, &data_path
);
68 PathService::Get(base::DIR_MODULE
, &data_path
);
70 DCHECK(!data_path
.empty());
72 *path_out
= data_path
.AppendASCII(file_name
);
73 #else // !defined(OS_MACOSX)
74 base::ScopedCFTypeRef
<CFStringRef
> natives_file_name(
75 base::SysUTF8ToCFStringRef(file_name
));
76 *path_out
= base::mac::PathForFrameworkBundleResource(natives_file_name
);
77 #endif // !defined(OS_MACOSX)
78 DCHECK(!path_out
->empty());
81 static bool MapV8File(base::PlatformFile platform_file
,
82 base::MemoryMappedFile::Region region
,
83 base::MemoryMappedFile
** mmapped_file_out
) {
84 DCHECK(*mmapped_file_out
== NULL
);
85 scoped_ptr
<base::MemoryMappedFile
> mmapped_file(new base::MemoryMappedFile());
86 if (mmapped_file
->Initialize(base::File(platform_file
), region
)) {
87 *mmapped_file_out
= mmapped_file
.release();
93 base::PlatformFile
OpenV8File(const char* file_name
,
94 base::MemoryMappedFile::Region
* region_out
) {
95 // Re-try logic here is motivated by http://crbug.com/479537
96 // for A/V on Windows (https://support.microsoft.com/en-us/kb/316609).
98 // These match tools/metrics/histograms.xml
99 enum OpenV8FileResult
{
107 GetV8FilePath(file_name
, &path
);
109 #if defined(OS_ANDROID)
110 base::File
file(base::android::OpenApkAsset(path
.value(), region_out
));
111 OpenV8FileResult result
= file
.IsValid() ? OpenV8FileResult::OPENED
112 : OpenV8FileResult::FAILED_OTHER
;
114 // Re-try logic here is motivated by http://crbug.com/479537
115 // for A/V on Windows (https://support.microsoft.com/en-us/kb/316609).
116 const int kMaxOpenAttempts
= 5;
117 const int kOpenRetryDelayMillis
= 250;
119 OpenV8FileResult result
= OpenV8FileResult::FAILED_IN_USE
;
120 int flags
= base::File::FLAG_OPEN
| base::File::FLAG_READ
;
122 for (int attempt
= 0; attempt
< kMaxOpenAttempts
; attempt
++) {
123 file
.Initialize(path
, flags
);
124 if (file
.IsValid()) {
125 *region_out
= base::MemoryMappedFile::Region::kWholeFile
;
127 result
= OpenV8FileResult::OPENED
;
130 result
= OpenV8FileResult::OPENED_RETRY
;
133 } else if (file
.error_details() != base::File::FILE_ERROR_IN_USE
) {
134 result
= OpenV8FileResult::FAILED_OTHER
;
136 // TODO(oth): temporary diagnostics for http://crbug.com/479537
137 std::string
narrow(kNativesFileName
);
138 base::FilePath::StringType
nativesBlob(narrow
.begin(), narrow
.end());
139 if (path
.BaseName().value() == nativesBlob
) {
140 base::File::Error file_error
= file
.error_details();
141 base::debug::Alias(&file_error
);
142 LOG(FATAL
) << "Failed to open V8 file '" << path
.value()
143 << "' (reason: " << file
.error_details() << ")";
147 } else if (kMaxOpenAttempts
- 1 != attempt
) {
148 base::PlatformThread::Sleep(
149 base::TimeDelta::FromMilliseconds(kOpenRetryDelayMillis
));
152 #endif // defined(OS_ANDROID)
154 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.OpenV8File.Result",
156 OpenV8FileResult::MAX_VALUE
);
157 return file
.TakePlatformFile();
160 void OpenNativesFileIfNecessary() {
161 if (g_natives_pf
== kInvalidPlatformFile
) {
162 g_natives_pf
= OpenV8File(kNativesFileName
, &g_natives_region
);
166 void OpenSnapshotFileIfNecessary() {
167 if (g_snapshot_pf
== kInvalidPlatformFile
) {
168 g_snapshot_pf
= OpenV8File(kSnapshotFileName
, &g_snapshot_region
);
172 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
173 bool VerifyV8StartupFile(base::MemoryMappedFile
** file
,
174 const unsigned char* fingerprint
) {
175 unsigned char output
[crypto::kSHA256Length
];
176 crypto::SHA256HashString(
177 base::StringPiece(reinterpret_cast<const char*>((*file
)->data()),
179 output
, sizeof(output
));
180 if (!memcmp(fingerprint
, output
, sizeof(output
))) {
184 // TODO(oth): Remove this temporary diagnostics for http://crbug.com/501799
185 uint64_t input
[sizeof(output
)];
186 memcpy(input
, fingerprint
, sizeof(input
));
188 base::debug::Alias(output
);
189 base::debug::Alias(input
);
191 const uint64_t* o64
= reinterpret_cast<const uint64_t*>(output
);
192 const uint64_t* f64
= reinterpret_cast<const uint64_t*>(fingerprint
);
193 LOG(FATAL
) << "Natives length " << (*file
)->length()
194 << " H(computed) " << o64
[0] << o64
[1] << o64
[2] << o64
[3]
195 << " H(expected) " << f64
[0] << f64
[1] << f64
[2] << f64
[3];
201 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
202 #endif // V8_USE_EXTERNAL_STARTUP_DATA
204 bool GenerateEntropy(unsigned char* buffer
, size_t amount
) {
205 base::RandBytes(buffer
, amount
);
211 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
212 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
213 // Defined in gen/gin/v8_snapshot_fingerprint.cc
214 extern const unsigned char g_natives_fingerprint
[];
215 extern const unsigned char g_snapshot_fingerprint
[];
216 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
218 enum LoadV8FileResult
{
222 V8_LOAD_FAILED_VERIFY
,
226 static LoadV8FileResult
MapVerify(base::PlatformFile platform_file
,
227 const base::MemoryMappedFile::Region
& region
,
228 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
229 const unsigned char* fingerprint
,
231 base::MemoryMappedFile
** mmapped_file_out
) {
232 if (platform_file
== kInvalidPlatformFile
)
233 return V8_LOAD_FAILED_OPEN
;
234 if (!MapV8File(platform_file
, region
, mmapped_file_out
))
235 return V8_LOAD_FAILED_MAP
;
236 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
237 if (!VerifyV8StartupFile(mmapped_file_out
, fingerprint
))
238 return V8_LOAD_FAILED_VERIFY
;
239 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
240 return V8_LOAD_SUCCESS
;
244 void V8Initializer::LoadV8Snapshot() {
245 if (g_mapped_snapshot
)
248 OpenSnapshotFileIfNecessary();
249 LoadV8FileResult result
= MapVerify(g_snapshot_pf
, g_snapshot_region
,
250 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
251 g_snapshot_fingerprint
,
254 // V8 can't start up without the source of the natives, but it can
255 // start up (slower) without the snapshot.
256 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.LoadV8Snapshot.Result", result
,
260 void V8Initializer::LoadV8Natives() {
261 if (g_mapped_natives
)
264 OpenNativesFileIfNecessary();
265 LoadV8FileResult result
= MapVerify(g_natives_pf
, g_natives_region
,
266 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
267 g_natives_fingerprint
,
270 if (result
!= V8_LOAD_SUCCESS
) {
271 LOG(FATAL
) << "Couldn't mmap v8 natives data file, status code is "
272 << static_cast<int>(result
);
277 void V8Initializer::LoadV8SnapshotFromFD(base::PlatformFile snapshot_pf
,
278 int64 snapshot_offset
,
279 int64 snapshot_size
) {
280 if (g_mapped_snapshot
)
283 if (snapshot_pf
== kInvalidPlatformFile
)
286 base::MemoryMappedFile::Region snapshot_region
=
287 base::MemoryMappedFile::Region::kWholeFile
;
288 if (snapshot_size
!= 0 || snapshot_offset
!= 0) {
289 snapshot_region
.offset
= snapshot_offset
;
290 snapshot_region
.size
= snapshot_size
;
293 LoadV8FileResult result
= V8_LOAD_SUCCESS
;
294 if (!MapV8File(snapshot_pf
, snapshot_region
, &g_mapped_snapshot
))
295 result
= V8_LOAD_FAILED_MAP
;
296 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
297 if (!VerifyV8StartupFile(&g_mapped_snapshot
, g_snapshot_fingerprint
))
298 result
= V8_LOAD_FAILED_VERIFY
;
299 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
300 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.LoadV8Snapshot.Result", result
,
305 void V8Initializer::LoadV8NativesFromFD(base::PlatformFile natives_pf
,
306 int64 natives_offset
,
307 int64 natives_size
) {
308 if (g_mapped_natives
)
311 CHECK_NE(natives_pf
, kInvalidPlatformFile
);
313 base::MemoryMappedFile::Region natives_region
=
314 base::MemoryMappedFile::Region::kWholeFile
;
315 if (natives_size
!= 0 || natives_offset
!= 0) {
316 natives_region
.offset
= natives_offset
;
317 natives_region
.size
= natives_size
;
320 if (!MapV8File(natives_pf
, natives_region
, &g_mapped_natives
)) {
321 LOG(FATAL
) << "Couldn't mmap v8 natives data file";
323 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
324 if (!VerifyV8StartupFile(&g_mapped_natives
, g_natives_fingerprint
)) {
325 LOG(FATAL
) << "Couldn't verify contents of v8 natives data file";
327 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
331 base::PlatformFile
V8Initializer::GetOpenNativesFileForChildProcesses(
332 base::MemoryMappedFile::Region
* region_out
) {
333 OpenNativesFileIfNecessary();
334 *region_out
= g_natives_region
;
339 base::PlatformFile
V8Initializer::GetOpenSnapshotFileForChildProcesses(
340 base::MemoryMappedFile::Region
* region_out
) {
341 OpenSnapshotFileIfNecessary();
342 *region_out
= g_snapshot_region
;
343 return g_snapshot_pf
;
345 #endif // defined(V8_USE_EXTERNAL_STARTUP_DATA)
348 void V8Initializer::Initialize(gin::IsolateHolder::ScriptMode mode
) {
349 static bool v8_is_initialized
= false;
350 if (v8_is_initialized
)
353 v8::V8::InitializePlatform(V8Platform::Get());
355 if (gin::IsolateHolder::kStrictMode
== mode
) {
356 static const char use_strict
[] = "--use_strict";
357 v8::V8::SetFlagsFromString(use_strict
, sizeof(use_strict
) - 1);
360 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
361 v8::StartupData natives
;
362 natives
.data
= reinterpret_cast<const char*>(g_mapped_natives
->data());
363 natives
.raw_size
= static_cast<int>(g_mapped_natives
->length());
364 v8::V8::SetNativesDataBlob(&natives
);
366 if (g_mapped_snapshot
!= NULL
) {
367 v8::StartupData snapshot
;
368 snapshot
.data
= reinterpret_cast<const char*>(g_mapped_snapshot
->data());
369 snapshot
.raw_size
= static_cast<int>(g_mapped_snapshot
->length());
370 v8::V8::SetSnapshotDataBlob(&snapshot
);
372 #endif // V8_USE_EXTERNAL_STARTUP_DATA
374 v8::V8::SetEntropySource(&GenerateEntropy
);
375 v8::V8::Initialize();
377 v8_is_initialized
= true;
381 void V8Initializer::GetV8ExternalSnapshotData(const char** natives_data_out
,
382 int* natives_size_out
,
383 const char** snapshot_data_out
,
384 int* snapshot_size_out
) {
385 if (g_mapped_natives
) {
386 *natives_data_out
= reinterpret_cast<const char*>(g_mapped_natives
->data());
387 *natives_size_out
= static_cast<int>(g_mapped_natives
->length());
389 *natives_data_out
= NULL
;
390 *natives_size_out
= 0;
392 if (g_mapped_snapshot
) {
394 reinterpret_cast<const char*>(g_mapped_snapshot
->data());
395 *snapshot_size_out
= static_cast<int>(g_mapped_snapshot
->length());
397 *snapshot_data_out
= NULL
;
398 *snapshot_size_out
= 0;