v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
embedded-data.cc
Go to the documentation of this file.
1// Copyright 2018 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
6
12
13namespace v8 {
14namespace internal {
15
17 if (!IsInCodeRange(address)) return Builtin::kNoBuiltinId;
18
19 // Note: Addresses within the padding section between builtins (i.e. within
20 // start + size <= address < start + padded_size) are interpreted as belonging
21 // to the preceding builtin.
22 uint32_t offset =
23 static_cast<uint32_t>(address - reinterpret_cast<Address>(RawCode()));
24
25 const struct BuiltinLookupEntry* start =
27 const struct BuiltinLookupEntry* end = start + kTableSize;
28 const struct BuiltinLookupEntry* desc =
29 std::upper_bound(start, end, offset,
30 [](uint32_t o, const struct BuiltinLookupEntry& desc) {
31 return o < desc.end_offset;
32 });
33 Builtin builtin = static_cast<Builtin>(desc->builtin_id);
34 DCHECK_LT(address,
36 DCHECK_GE(address, InstructionStartOf(builtin));
37 return builtin;
38}
39
40// static
42 // Mksnapshot calls this while the embedded blob is not available yet.
43 if (isolate->embedded_blob_code() == nullptr) return false;
45
46 if (EmbeddedData::FromBlob(isolate).IsInCodeRange(pc)) return true;
47 return isolate->is_short_builtin_calls_enabled() &&
49}
50
51// static
53 Isolate* isolate, Address address, uint32_t* hashable_address) {
54 // Mksnapshot calls this while the embedded blob is not available yet.
55 if (isolate->embedded_blob_code() == nullptr) return false;
57
59 if (d.IsInCodeRange(address)) {
60 *hashable_address = d.AddressForHashing(address);
61 return true;
62 }
63
64 if (isolate->is_short_builtin_calls_enabled()) {
66 if (d.IsInCodeRange(address)) {
67 *hashable_address = d.AddressForHashing(address);
68 return true;
69 }
70 }
71 return false;
72}
73
74// static
76 Address address) {
77 // Mksnapshot calls this while the embedded blob is not available yet.
78 if (isolate->embedded_blob_code() == nullptr) return Builtin::kNoBuiltinId;
80
81 Builtin builtin = EmbeddedData::FromBlob(isolate).TryLookupCode(address);
82
83 if (isolate->is_short_builtin_calls_enabled() &&
84 !Builtins::IsBuiltinId(builtin)) {
85 builtin = EmbeddedData::FromBlob().TryLookupCode(address);
86 }
87
88#ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE
90 // When shared pointer compression cage is enabled and it has the embedded
91 // code blob copy then it could have been used regardless of whether the
92 // isolate uses it or knows about it or not (see
93 // InstructionStream::OffHeapInstructionStart()).
94 // So, this blob has to be checked too.
96 if (code_range && code_range->embedded_blob_code_copy() != nullptr) {
97 builtin = EmbeddedData::FromBlob(code_range).TryLookupCode(address);
98 }
99 }
100#endif
101 return builtin;
102}
103
104// static
106 Isolate* isolate, uint8_t** code, uint32_t* code_size, uint8_t** data,
107 uint32_t* data_size) {
108 // Create the embedded blob from scratch using the current Isolate's heap.
110
111 // Allocate the backing store that will contain the embedded blob in this
112 // Isolate. The backing store is on the native heap, *not* on V8's garbage-
113 // collected heap.
115 const uint32_t alignment =
116 static_cast<uint32_t>(page_allocator->AllocatePageSize());
117
118 void* const requested_allocation_code_address =
119 AlignedAddress(isolate->heap()->GetRandomMmapAddr(), alignment);
120 const uint32_t allocation_code_size = RoundUp(d.code_size(), alignment);
121 uint8_t* allocated_code_bytes = static_cast<uint8_t*>(AllocatePages(
122 page_allocator, requested_allocation_code_address, allocation_code_size,
123 alignment, PageAllocator::kReadWrite));
124 CHECK_NOT_NULL(allocated_code_bytes);
125
126 void* const requested_allocation_data_address =
127 AlignedAddress(isolate->heap()->GetRandomMmapAddr(), alignment);
128 const uint32_t allocation_data_size = RoundUp(d.data_size(), alignment);
129 uint8_t* allocated_data_bytes = static_cast<uint8_t*>(AllocatePages(
130 page_allocator, requested_allocation_data_address, allocation_data_size,
131 alignment, PageAllocator::kReadWrite));
132 CHECK_NOT_NULL(allocated_data_bytes);
133
134 // Copy the embedded blob into the newly allocated backing store. Switch
135 // permissions to read-execute since builtin code is immutable from now on
136 // and must be executable in case any JS execution is triggered.
137 //
138 // Once this backing store is set as the current_embedded_blob, V8 cannot tell
139 // the difference between a 'real' embedded build (where the blob is embedded
140 // in the binary) and what we are currently setting up here (where the blob is
141 // on the native heap).
142 std::memcpy(allocated_code_bytes, d.code(), d.code_size());
143 if (v8_flags.experimental_flush_embedded_blob_icache) {
144 FlushInstructionCache(allocated_code_bytes, d.code_size());
145 }
146 CHECK(SetPermissions(page_allocator, allocated_code_bytes,
147 allocation_code_size, PageAllocator::kReadExecute));
148
149 std::memcpy(allocated_data_bytes, d.data(), d.data_size());
150 CHECK(SetPermissions(page_allocator, allocated_data_bytes,
151 allocation_data_size, PageAllocator::kRead));
152
153 *code = allocated_code_bytes;
154 *code_size = d.code_size();
155 *data = allocated_data_bytes;
156 *data_size = d.data_size();
157
158 d.Dispose();
159}
160
161// static
163 uint8_t* code, uint32_t code_size, uint8_t* data, uint32_t data_size) {
165 const uint32_t page_size =
166 static_cast<uint32_t>(page_allocator->AllocatePageSize());
167 FreePages(page_allocator, code, RoundUp(code_size, page_size));
168 FreePages(page_allocator, data, RoundUp(data_size, page_size));
169}
170
171namespace {
172
173void FinalizeEmbeddedCodeTargets(Isolate* isolate, EmbeddedData* blob) {
174 static const int kRelocMask =
175 RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
176 RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET);
177
178 static_assert(Builtins::kAllBuiltinsAreIsolateIndependent);
179 for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
180 ++builtin) {
181 Tagged<Code> code = isolate->builtins()->code(builtin);
182 RelocIterator on_heap_it(code, kRelocMask);
183 RelocIterator off_heap_it(blob, code, kRelocMask);
184
185#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64) || \
186 defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_IA32) || \
187 defined(V8_TARGET_ARCH_S390X) || defined(V8_TARGET_ARCH_RISCV64) || \
188 defined(V8_TARGET_ARCH_LOONG64) || defined(V8_TARGET_ARCH_RISCV32)
189 // On these platforms we emit relative builtin-to-builtin
190 // jumps for isolate independent builtins in the snapshot. This fixes up the
191 // relative jumps to the right offsets in the snapshot.
192 // See also: InstructionStream::IsIsolateIndependent.
193 while (!on_heap_it.done()) {
194 DCHECK(!off_heap_it.done());
195
196 RelocInfo* rinfo = on_heap_it.rinfo();
197 DCHECK_EQ(rinfo->rmode(), off_heap_it.rinfo()->rmode());
198 Tagged<Code> target_code =
199 Code::FromTargetAddress(rinfo->target_address());
200 CHECK(Builtins::IsIsolateIndependentBuiltin(target_code));
201
202 // Do not emit write-barrier for off-heap writes.
203 off_heap_it.rinfo()->set_off_heap_target_address(
204 blob->InstructionStartOf(target_code->builtin_id()));
205
206 on_heap_it.next();
207 off_heap_it.next();
208 }
209 DCHECK(off_heap_it.done());
210#else
211 // Architectures other than x64 and arm/arm64 do not use pc-relative calls
212 // and thus must not contain embedded code targets. Instead, we use an
213 // indirection through the root register.
214 CHECK(on_heap_it.done());
215 CHECK(off_heap_it.done());
216#endif
217 }
218}
219
220void EnsureRelocatable(Tagged<Code> code) {
221 if (code->relocation_size() == 0) return;
222
223 // On some architectures (arm) the builtin might have a non-empty reloc
224 // info containing a CONST_POOL entry. These entries don't have to be
225 // updated when InstructionStream object is relocated, so it's safe to drop
226 // the reloc info alltogether. If it wasn't the case then we'd have to store
227 // it in the metadata.
228 for (RelocIterator it(code); !it.done(); it.next()) {
229 CHECK_EQ(it.rinfo()->rmode(), RelocInfo::CONST_POOL);
230 }
231}
232
233} // namespace
234
235// static
237 Builtins* builtins = isolate->builtins();
238
239 // Store instruction stream lengths and offsets.
240 std::vector<struct LayoutDescription> layout_descriptions(kTableSize);
241 std::vector<struct BuiltinLookupEntry> offset_descriptions(kTableSize);
242
243 bool saw_unsafe_builtin = false;
244 uint32_t raw_code_size = 0;
245 uint32_t raw_data_size = 0;
247
248 std::vector<Builtin> reordered_builtins;
249 if (v8_flags.reorder_builtins &&
251 DCHECK(v8_flags.turbo_profiling_input.value());
252 // TODO(ishell, v8:13938): avoid the binary size overhead for non-mksnapshot
253 // binaries.
254 BuiltinsSorter sorter;
255 std::vector<uint32_t> builtin_sizes;
257 Tagged<Code> code = builtins->code(i);
258 uint32_t instruction_size =
259 static_cast<uint32_t>(code->instruction_size());
260 uint32_t padding_size = PadAndAlignCode(instruction_size);
261 builtin_sizes.push_back(padding_size);
262 }
263 reordered_builtins = sorter.SortBuiltins(
264 v8_flags.turbo_profiling_input.value(), builtin_sizes);
265 CHECK_EQ(reordered_builtins.size(), Builtins::kBuiltinCount);
266 }
267
268 for (ReorderedBuiltinIndex embedded_index = 0;
269 embedded_index < Builtins::kBuiltinCount; embedded_index++) {
271 if (reordered_builtins.empty()) {
272 builtin = static_cast<Builtin>(embedded_index);
273 } else {
274 builtin = reordered_builtins[embedded_index];
275 }
276 Tagged<Code> code = builtins->code(builtin);
277
278 // Sanity-check that the given builtin is isolate-independent.
279 if (!code->IsIsolateIndependent(isolate)) {
280 saw_unsafe_builtin = true;
281 fprintf(stderr, "%s is not isolate-independent.\n",
282 Builtins::name(builtin));
283 }
284
285 uint32_t instruction_size = static_cast<uint32_t>(code->instruction_size());
286 DCHECK_EQ(0, raw_code_size % kCodeAlignment);
287 {
288 // We use builtin id as index in layout_descriptions.
289 const int builtin_id = static_cast<int>(builtin);
290 struct LayoutDescription& layout_desc = layout_descriptions[builtin_id];
291 layout_desc.instruction_offset = raw_code_size;
292 layout_desc.instruction_length = instruction_size;
293 layout_desc.metadata_offset = raw_data_size;
294 }
295 // Align the start of each section.
296 raw_code_size += PadAndAlignCode(instruction_size);
297 raw_data_size += PadAndAlignData(code->metadata_size());
298
299 {
300 // We use embedded index as index in offset_descriptions.
301 struct BuiltinLookupEntry& offset_desc =
302 offset_descriptions[embedded_index];
303 offset_desc.end_offset = raw_code_size;
304 offset_desc.builtin_id = static_cast<uint32_t>(builtin);
305 }
306 }
308 !saw_unsafe_builtin,
309 "One or more builtins marked as isolate-independent either contains "
310 "isolate-dependent code or aliases the off-heap trampoline register. "
311 "If in doubt, ask jgruber@");
312
313 // Allocate space for the code section, value-initialized to 0.
314 static_assert(RawCodeOffset() == 0);
315 const uint32_t blob_code_size = RawCodeOffset() + raw_code_size;
316 uint8_t* const blob_code = new uint8_t[blob_code_size]();
317
318 // Allocate space for the data section, value-initialized to 0.
319 static_assert(
321 const uint32_t blob_data_size = FixedDataSize() + raw_data_size;
322 uint8_t* const blob_data = new uint8_t[blob_data_size]();
323
324 // Initially zap the entire blob, effectively padding the alignment area
325 // between two builtins with int3's (on x64/ia32).
326 ZapCode(reinterpret_cast<Address>(blob_code), blob_code_size);
327
328 // Hash relevant parts of the Isolate's heap and store the result.
329 {
330 static_assert(IsolateHashSize() == kSizetSize);
331 const size_t hash = isolate->HashIsolateForEmbeddedBlob();
332 std::memcpy(blob_data + IsolateHashOffset(), &hash, IsolateHashSize());
333 }
334
335 // Write the layout_descriptions tables.
337 sizeof(layout_descriptions[0]) * layout_descriptions.size());
338 std::memcpy(blob_data + LayoutDescriptionTableOffset(),
339 layout_descriptions.data(), LayoutDescriptionTableSize());
340
341 // Write the builtin_offset_descriptions tables.
343 sizeof(offset_descriptions[0]) * offset_descriptions.size());
344 std::memcpy(blob_data + BuiltinLookupEntryTableOffset(),
345 offset_descriptions.data(), BuiltinLookupEntryTableSize());
346
347 // .. and the variable-size data section.
348 uint8_t* const raw_metadata_start = blob_data + RawMetadataOffset();
350 for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
351 ++builtin) {
352 Tagged<Code> code = builtins->code(builtin);
353 uint32_t offset =
354 layout_descriptions[static_cast<int>(builtin)].metadata_offset;
355 uint8_t* dst = raw_metadata_start + offset;
356 DCHECK_LE(RawMetadataOffset() + offset + code->metadata_size(),
357 blob_data_size);
358 std::memcpy(dst, reinterpret_cast<uint8_t*>(code->metadata_start()),
359 code->metadata_size());
360 }
363 static_cast<size_t>(raw_code_size) <= kMaxPCRelativeCodeRangeInMB * MB);
364
365 // .. and the variable-size code section.
366 uint8_t* const raw_code_start = blob_code + RawCodeOffset();
368 for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
369 ++builtin) {
370 Tagged<Code> code = builtins->code(builtin);
371 uint32_t offset =
372 layout_descriptions[static_cast<int>(builtin)].instruction_offset;
373 uint8_t* dst = raw_code_start + offset;
374 DCHECK_LE(RawCodeOffset() + offset + code->instruction_size(),
375 blob_code_size);
376 std::memcpy(dst, reinterpret_cast<uint8_t*>(code->instruction_start()),
377 code->instruction_size());
378 }
379
380 EmbeddedData d(blob_code, blob_code_size, blob_data, blob_data_size);
381
382 // Fix up call targets that point to other embedded builtins.
383 FinalizeEmbeddedCodeTargets(isolate, &d);
384
385 // Hash the blob and store the result.
386 {
387 static_assert(EmbeddedBlobDataHashSize() == kSizetSize);
388 const size_t data_hash = d.CreateEmbeddedBlobDataHash();
389 std::memcpy(blob_data + EmbeddedBlobDataHashOffset(), &data_hash,
391
392 static_assert(EmbeddedBlobCodeHashSize() == kSizetSize);
393 const size_t code_hash = d.CreateEmbeddedBlobCodeHash();
394 std::memcpy(blob_data + EmbeddedBlobCodeHashOffset(), &code_hash,
396
397 DCHECK_EQ(data_hash, d.CreateEmbeddedBlobDataHash());
398 DCHECK_EQ(data_hash, d.EmbeddedBlobDataHash());
399 DCHECK_EQ(code_hash, d.CreateEmbeddedBlobCodeHash());
400 DCHECK_EQ(code_hash, d.EmbeddedBlobCodeHash());
401 }
402
403 if (DEBUG_BOOL) {
404 for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
405 ++builtin) {
406 Tagged<Code> code = builtins->code(builtin);
407 CHECK_EQ(d.InstructionSizeOf(builtin), code->instruction_size());
408 }
409 }
410
411 // Ensure that InterpreterEntryTrampolineForProfiling is relocatable.
412 // See v8_flags.interpreted_frames_native_stack for details.
413 EnsureRelocatable(
414 builtins->code(Builtin::kInterpreterEntryTrampolineForProfiling));
415
416 if (v8_flags.serialization_statistics) d.PrintStatistics();
417
418 return d;
419}
420
422 static_assert(EmbeddedBlobDataHashOffset() == 0);
424 static_assert(IsolateHashOffset() ==
426 static constexpr uint32_t kFirstHashedDataOffset = IsolateHashOffset();
427 // Hash the entire data section except the embedded blob hash fields
428 // themselves.
429 base::Vector<const uint8_t> payload(data_ + kFirstHashedDataOffset,
430 data_size_ - kFirstHashedDataOffset);
431 return Checksum(payload);
432}
433
435 CHECK(v8_flags.text_is_readable);
437 return Checksum(payload);
438}
439
441 Builtin builtin =
442 Builtins::FromInt(BuiltinLookupEntry(embedded_index)->builtin_id);
443 return builtin;
444}
445
447 DCHECK(v8_flags.serialization_statistics);
448
449 constexpr int kCount = Builtins::kBuiltinCount;
450 int sizes[kCount];
452 for (int i = 0; i < kCount; i++) {
454 }
455
456 // Sort for percentiles.
457 std::sort(&sizes[0], &sizes[kCount]);
458
459 const int k50th = kCount * 0.5;
460 const int k75th = kCount * 0.75;
461 const int k90th = kCount * 0.90;
462 const int k99th = kCount * 0.99;
463
464 PrintF("EmbeddedData:\n");
465 PrintF(" Total size: %d\n",
466 static_cast<int>(code_size() + data_size()));
467 PrintF(" Data size: %d\n", static_cast<int>(data_size()));
468 PrintF(" Code size: %d\n", static_cast<int>(code_size()));
469 PrintF(" Instruction size (50th percentile): %d\n", sizes[k50th]);
470 PrintF(" Instruction size (75th percentile): %d\n", sizes[k75th]);
471 PrintF(" Instruction size (90th percentile): %d\n", sizes[k90th]);
472 PrintF(" Instruction size (99th percentile): %d\n", sizes[k99th]);
473 PrintF("\n");
474}
475
476} // namespace internal
477} // namespace v8
virtual size_t AllocatePageSize()=0
V8_INLINE bool all_hash_matched() const
static BuiltinsCallGraph * Get()
std::vector< Builtin > SortBuiltins(const char *profiling_file, const std::vector< uint32_t > &builtin_size)
static constexpr int kBuiltinCount
Definition builtins.h:105
static constexpr Builtin kFirst
Definition builtins.h:112
static constexpr bool kAllBuiltinsAreIsolateIndependent
Definition builtins.h:262
static constexpr bool IsBuiltinId(Builtin builtin)
Definition builtins.h:128
static constexpr Builtin FromInt(int id)
Definition builtins.h:140
static constexpr Builtin kLast
Definition builtins.h:113
static V8_EXPORT_PRIVATE const char * name(Builtin builtin)
Definition builtins.cc:226
uint8_t * embedded_blob_code_copy() const
Definition code-range.h:83
static constexpr uint32_t FixedDataSize()
static EmbeddedData NewFromIsolate(Isolate *isolate)
bool IsInCodeRange(Address pc) const
const BuiltinLookupEntry * BuiltinLookupEntry(ReorderedBuiltinIndex index) const
Address InstructionStartOf(Builtin builtin) const
static constexpr uint32_t BuiltinLookupEntryTableOffset()
uint32_t InstructionSizeOf(Builtin builtin) const
uint32_t PaddedInstructionSizeOf(Builtin builtin) const
static EmbeddedData FromBlob()
static constexpr uint32_t LayoutDescriptionTableSize()
static constexpr uint32_t EmbeddedBlobDataHashOffset()
static constexpr uint32_t EmbeddedBlobCodeHashOffset()
size_t CreateEmbeddedBlobDataHash() const
static constexpr uint32_t IsolateHashOffset()
static constexpr uint32_t LayoutDescriptionTableOffset()
size_t CreateEmbeddedBlobCodeHash() const
static constexpr uint32_t RawMetadataOffset()
static constexpr int PadAndAlignCode(int size)
static constexpr uint32_t BuiltinLookupEntryTableSize()
const uint8_t * RawCode() const
Builtin TryLookupCode(Address address) const
Builtin GetBuiltinId(ReorderedBuiltinIndex embedded_index) const
static constexpr uint32_t kTableSize
static constexpr uint32_t IsolateHashSize()
static constexpr uint32_t EmbeddedBlobCodeHashSize()
static constexpr uint32_t EmbeddedBlobDataHashSize()
static constexpr int PadAndAlignData(int size)
static constexpr uint32_t RawCodeOffset()
static constexpr int kMetadataAlignment
static IsolateGroup * current()
CodeRange * GetCodeRange() const
static const uint8_t * CurrentEmbeddedBlobCode()
Definition isolate.cc:395
static void FreeOffHeapOffHeapInstructionStream(uint8_t *code, uint32_t code_size, uint8_t *data, uint32_t data_size)
static bool TryGetAddressForHashing(Isolate *isolate, Address address, uint32_t *hashable_address)
static void CreateOffHeapOffHeapInstructionStream(Isolate *isolate, uint8_t **code, uint32_t *code_size, uint8_t **data, uint32_t *data_size)
static Builtin TryLookupCode(Isolate *isolate, Address address)
static bool PcIsOffHeap(Isolate *isolate, Address pc)
V8_INLINE Address target_address()
#define DEBUG_BOOL
Definition globals.h:87
int start
int end
#define V8_SHORT_BUILTIN_CALLS_BOOL
int32_t offset
Builtin builtin
bool SetPermissions(v8::PageAllocator *page_allocator, void *address, size_t size, PageAllocator::Permission access)
v8::PageAllocator * GetPlatformPageAllocator()
Definition allocation.cc:66
V8_INLINE void ZapCode(Address addr, size_t size_in_bytes)
Definition utils.h:839
void PrintF(const char *format,...)
Definition utils.cc:39
void * AllocatePages(v8::PageAllocator *page_allocator, void *hint, size_t size, size_t alignment, PageAllocator::Permission access)
void FlushInstructionCache(void *start, size_t size)
constexpr intptr_t kCodeAlignment
Definition globals.h:964
uint32_t ReorderedBuiltinIndex
constexpr size_t kMaxPCRelativeCodeRangeInMB
V8_EXPORT_PRIVATE FlagValues v8_flags
constexpr int kSizetSize
Definition globals.h:404
void FreePages(v8::PageAllocator *page_allocator, void *address, const size_t size)
uint32_t Checksum(base::Vector< const uint8_t > payload)
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK_IMPLIES(lhs, rhs)
#define CHECK(condition)
Definition logging.h:124
#define CHECK_WITH_MSG(condition, message)
Definition logging.h:118
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define CHECK_NOT_NULL(val)
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
constexpr T RoundUp(T x, intptr_t m)
Definition macros.h:387
void * AlignedAddress(void *address, size_t alignment)
Definition macros.h:407
constexpr bool IsAligned(T value, U alignment)
Definition macros.h:403