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
5#ifndef V8_SNAPSHOT_EMBEDDED_EMBEDDED_DATA_H_
6#define V8_SNAPSHOT_EMBEDDED_EMBEDDED_DATA_H_
7
8#include "src/base/macros.h"
9#include "src/builtins/builtins.h"
10#include "src/common/globals.h"
11#include "src/execution/isolate.h"
12#include "src/heap/code-range.h"
13
14namespace v8 {
15namespace internal {
16
17class Code;
18class Isolate;
19
20// Wraps an off-heap instruction stream.
21// TODO(jgruber,v8:6666): Remove this class.
22class OffHeapInstructionStream final : public AllStatic {
23 public:
24  // Returns true, iff the given pc points into an off-heap instruction stream.
25  static bool PcIsOffHeap(Isolate* isolate, Address pc);
26
27  // If the address belongs to the embedded code blob, predictably converts it
28  // to uint32 by calculating offset from the embedded code blob start and
29  // returns true, and false otherwise.
30  static bool TryGetAddressForHashing(Isolate* isolate, Address address,
31                                      uint32_t* hashable_address);
32
33  // Returns the corresponding builtin ID if lookup succeeds, and kNoBuiltinId
34  // otherwise.
35  static Builtin TryLookupCode(Isolate* isolate, Address address);
36
37  // During snapshot creation, we first create an executable off-heap area
38  // containing all off-heap code. The area is guaranteed to be contiguous.
39  // Note that this only applies when building the snapshot, e.g. for
40  // mksnapshot. Otherwise, off-heap code is embedded directly into the binary.
41  static void CreateOffHeapOffHeapInstructionStream(Isolate* isolate,
42                                                    uint8_t** code,
43                                                    uint32_t* code_size,
44                                                    uint8_t** data,
45                                                    uint32_t* data_size);
46  static void FreeOffHeapOffHeapInstructionStream(uint8_t* code,
47                                                  uint32_t code_size,
48                                                  uint8_t* data,
49                                                  uint32_t data_size);
50};
51
52class EmbeddedData final {
53 public:
54  static EmbeddedData FromIsolate(Isolate* isolate);
55
56  static EmbeddedData FromBlob() {
57    return EmbeddedData(Isolate::CurrentEmbeddedBlobCode(),
58                        Isolate::CurrentEmbeddedBlobCodeSize(),
59                        Isolate::CurrentEmbeddedBlobData(),
60                        Isolate::CurrentEmbeddedBlobDataSize());
61  }
62
63  static EmbeddedData FromBlob(Isolate* isolate) {
64    return EmbeddedData(
65        isolate->embedded_blob_code(), isolate->embedded_blob_code_size(),
66        isolate->embedded_blob_data(), isolate->embedded_blob_data_size());
67  }
68
69  static EmbeddedData FromBlob(CodeRange* code_range) {
70    return EmbeddedData(code_range->embedded_blob_code_copy(),
71                        Isolate::CurrentEmbeddedBlobCodeSize(),
72                        Isolate::CurrentEmbeddedBlobData(),
73                        Isolate::CurrentEmbeddedBlobDataSize());
74  }
75
76  const uint8_t* code() const { return code_; }
77  uint32_t code_size() const { return code_size_; }
78  const uint8_t* data() const { return data_; }
79  uint32_t data_size() const { return data_size_; }
80
81  bool IsInCodeRange(Address pc) const {
82    Address start = reinterpret_cast<Address>(code_);
83    return (start <= pc) && (pc < start + code_size_);
84  }
85
86  // When short builtin calls optimization is enabled for the Isolate, there
87  // will be two builtins instruction streams executed: the embedded one and
88  // the one un-embedded into the per-Isolate code range. In most of the cases,
89  // the per-Isolate instructions will be used but in some cases (like builtin
90  // calls from Wasm) the embedded instruction stream could be used.
91  // If the requested PC belongs to the embedded code blob - it'll be returned,
92  // and the per-Isolate blob otherwise.
93  // See http://crbug.com/v8/11527 for details.
94  inline static EmbeddedData GetEmbeddedDataForPC(Isolate* isolate,
95                                                  Address maybe_builtin_pc) {
96    EmbeddedData d = EmbeddedData::FromBlob(isolate);
97    if (isolate->is_short_builtin_calls_enabled() &&
98        !d.IsInCodeRange(maybe_builtin_pc)) {
99      EmbeddedData global_d = EmbeddedData::FromBlob();
100      // If the pc does not belong to the embedded code blob we should be using
101      // the un-embedded one.
102      if (global_d.IsInCodeRange(maybe_builtin_pc)) return global_d;
103    }
104#ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE
105    if (V8_SHORT_BUILTIN_CALLS_BOOL && !d.IsInCodeRange(maybe_builtin_pc)) {
106      // When shared pointer compression cage is enabled and it has the embedded
107      // code blob copy then it could have been used regardless of whether the
108      // isolate uses it or knows about it or not (see
109      // Code::OffHeapInstructionStart()).
110      // So, this blob has to be checked too.
111      CodeRange* code_range = CodeRange::GetProcessWideCodeRange().get();
112      if (code_range && code_range->embedded_blob_code_copy() != nullptr) {
113        EmbeddedData remapped_d = EmbeddedData::FromBlob(code_range);
114        // If the pc does not belong to the embedded code blob we should be
115        // using the un-embedded one.
116        if (remapped_d.IsInCodeRange(maybe_builtin_pc)) return remapped_d;
117      }
118    }
119#endif
120    return d;
121  }
122
123  void Dispose() {
124    delete[] code_;
125    code_ = nullptr;
126    delete[] data_;
127    data_ = nullptr;
128  }
129
130  // TODO(ishell): rename XyzOfBuiltin() to XyzOf().
131  inline Address InstructionStartOfBuiltin(Builtin builtin) const;
132  inline uint32_t InstructionSizeOfBuiltin(Builtin builtin) const;
133
134  inline Address InstructionStartOfBytecodeHandlers() const;
135  inline Address InstructionEndOfBytecodeHandlers() const;
136
137  inline Address MetadataStartOfBuiltin(Builtin builtin) const;
138  inline uint32_t MetadataSizeOfBuiltin(Builtin builtin) const;
139
140  inline Address SafepointTableStartOf(Builtin builtin) const;
141  inline uint32_t SafepointTableSizeOf(Builtin builtin) const;
142
143  inline Address HandlerTableStartOf(Builtin builtin) const;
144  inline uint32_t HandlerTableSizeOf(Builtin builtin) const;
145
146  inline Address ConstantPoolStartOf(Builtin builtin) const;
147  inline uint32_t ConstantPoolSizeOf(Builtin builtin) const;
148
149  inline Address CodeCommentsStartOf(Builtin builtin) const;
150  inline uint32_t CodeCommentsSizeOf(Builtin builtin) const;
151
152  inline Address UnwindingInfoStartOf(Builtin builtin) const;
153  inline uint32_t UnwindingInfoSizeOf(Builtin builtin) const;
154
155  uint32_t AddressForHashing(Address addr) {
156    DCHECK(IsInCodeRange(addr));
157    Address start = reinterpret_cast<Address>(code_);
158    return static_cast<uint32_t>(addr - start);
159  }
160
161  // Padded with kCodeAlignment.
162  // TODO(v8:11045): Consider removing code alignment.
163  inline uint32_t PaddedInstructionSizeOfBuiltin(Builtin builtin) const;
164
165  size_t CreateEmbeddedBlobDataHash() const;
166  size_t CreateEmbeddedBlobCodeHash() const;
167  size_t EmbeddedBlobDataHash() const {
168    return *reinterpret_cast<const size_t*>(data_ +
169                                            EmbeddedBlobDataHashOffset());
170  }
171  size_t EmbeddedBlobCodeHash() const {
172    return *reinterpret_cast<const size_t*>(data_ +
173                                            EmbeddedBlobCodeHashOffset());
174  }
175
176  size_t IsolateHash() const {
177    return *reinterpret_cast<const size_t*>(data_ + IsolateHashOffset());
178  }
179
180  // Blob layout information for a single instruction stream. Corresponds
181  // roughly to Code object layout (see the instruction and metadata area).
182  struct LayoutDescription {
183    // The offset and (unpadded) length of this builtin's instruction area
184    // from the start of the embedded code section.
185    uint32_t instruction_offset;
186    uint32_t instruction_length;
187    // The offset and (unpadded) length of this builtin's metadata area
188    // from the start of the embedded data section.
189    uint32_t metadata_offset;
190    uint32_t metadata_length;
191
192    // The offsets describing inline metadata tables, relative to the start
193    // of the embedded data section.
194    uint32_t handler_table_offset;
195#if V8_EMBEDDED_CONSTANT_POOL
196    uint32_t constant_pool_offset;
197#endif
198    uint32_t code_comments_offset_offset;
199    uint32_t unwinding_info_offset_offset;
200  };
201  STATIC_ASSERT(offsetof(LayoutDescription, instruction_offset) ==
202                0 * kUInt32Size);
203  STATIC_ASSERT(offsetof(LayoutDescription, instruction_length) ==
204                1 * kUInt32Size);
205  STATIC_ASSERT(offsetof(LayoutDescription, metadata_offset) ==
206                2 * kUInt32Size);
207  STATIC_ASSERT(offsetof(LayoutDescription, metadata_length) ==
208                3 * kUInt32Size);
209  STATIC_ASSERT(offsetof(LayoutDescription, handler_table_offset) ==
210                4 * kUInt32Size);
211#if V8_EMBEDDED_CONSTANT_POOL
212  STATIC_ASSERT(offsetof(LayoutDescription, constant_pool_offset) ==
213                5 * kUInt32Size);
214  STATIC_ASSERT(offsetof(LayoutDescription, code_comments_offset_offset) ==
215                6 * kUInt32Size);
216  STATIC_ASSERT(offsetof(LayoutDescription, unwinding_info_offset_offset) ==
217                7 * kUInt32Size);
218  STATIC_ASSERT(sizeof(LayoutDescription) == 8 * kUInt32Size);
219#else
220  STATIC_ASSERT(offsetof(LayoutDescription, code_comments_offset_offset) ==
221                5 * kUInt32Size);
222  STATIC_ASSERT(offsetof(LayoutDescription, unwinding_info_offset_offset) ==
223                6 * kUInt32Size);
224  STATIC_ASSERT(sizeof(LayoutDescription) == 7 * kUInt32Size);
225#endif
226
227  // The layout of the blob is as follows:
228  //
229  // data:
230  // [0] hash of the data section
231  // [1] hash of the code section
232  // [2] hash of embedded-blob-relevant heap objects
233  // [3] layout description of instruction stream 0
234  // ... layout descriptions
235  // [x] metadata section of builtin 0
236  // ... metadata sections
237  //
238  // code:
239  // [0] instruction section of builtin 0
240  // ... instruction sections
241
242  static constexpr uint32_t kTableSize = Builtins::kBuiltinCount;
243  static constexpr uint32_t EmbeddedBlobDataHashOffset() { return 0; }
244  static constexpr uint32_t EmbeddedBlobDataHashSize() { return kSizetSize; }
245  static constexpr uint32_t EmbeddedBlobCodeHashOffset() {
246    return EmbeddedBlobDataHashOffset() + EmbeddedBlobDataHashSize();
247  }
248  static constexpr uint32_t EmbeddedBlobCodeHashSize() { return kSizetSize; }
249  static constexpr uint32_t IsolateHashOffset() {
250    return EmbeddedBlobCodeHashOffset() + EmbeddedBlobCodeHashSize();
251  }
252  static constexpr uint32_t IsolateHashSize() { return kSizetSize; }
253  static constexpr uint32_t LayoutDescriptionTableOffset() {
254    return IsolateHashOffset() + IsolateHashSize();
255  }
256  static constexpr uint32_t LayoutDescriptionTableSize() {
257    return sizeof(struct LayoutDescription) * kTableSize;
258  }
259  static constexpr uint32_t FixedDataSize() {
260    return LayoutDescriptionTableOffset() + LayoutDescriptionTableSize();
261  }
262  // The variable-size data section starts here.
263  static constexpr uint32_t RawMetadataOffset() { return FixedDataSize(); }
264
265  // Code is in its own dedicated section.
266  static constexpr uint32_t RawCodeOffset() { return 0; }
267
268 private:
269  EmbeddedData(const uint8_t* code, uint32_t code_size, const uint8_t* data,
270               uint32_t data_size)
271      : code_(code), code_size_(code_size), data_(data), data_size_(data_size) {
272    DCHECK_NOT_NULL(code);
273    DCHECK_LT(0, code_size);
274    DCHECK_NOT_NULL(data);
275    DCHECK_LT(0, data_size);
276  }
277
278  const uint8_t* RawCode() const { return code_ + RawCodeOffset(); }
279
280  const LayoutDescription& LayoutDescription(Builtin builtin) const {
281    const struct LayoutDescription* descs =
282        reinterpret_cast<const struct LayoutDescription*>(
283            data_ + LayoutDescriptionTableOffset());
284    return descs[static_cast<int>(builtin)];
285  }
286  const uint8_t* RawMetadata() const { return data_ + RawMetadataOffset(); }
287
288  static constexpr int PadAndAlignCode(int size) {
289    // Ensure we have at least one byte trailing the actual builtin
290    // instructions which we can later fill with int3.
291    return RoundUp<kCodeAlignment>(size + 1);
292  }
293  static constexpr int PadAndAlignData(int size) {
294    // Ensure we have at least one byte trailing the actual builtin
295    // instructions which we can later fill with int3.
296    return RoundUp<Code::kMetadataAlignment>(size);
297  }
298
299  void PrintStatistics() const;
300
301  // The code section contains instruction streams. It is guaranteed to have
302  // execute permissions, and may have read permissions.
303  const uint8_t* code_;
304  uint32_t code_size_;
305
306  // The data section contains both descriptions of the code section (hashes,
307  // offsets, sizes) and metadata describing Code objects (see
308  // Code::MetadataStart()). It is guaranteed to have read permissions.
309  const uint8_t* data_;
310  uint32_t data_size_;
311};
312
313}  // namespace internal
314}  // namespace v8
315
316#endif  // V8_SNAPSHOT_EMBEDDED_EMBEDDED_DATA_H_
317