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#include "src/snapshot/embedded/embedded-file-writer.h"
6
7#include <algorithm>
8#include <cinttypes>
9
10#include "src/codegen/source-position-table.h"
11#include "src/flags/flags.h"  // For ENABLE_CONTROL_FLOW_INTEGRITY_BOOL
12#include "src/objects/code-inl.h"
13#include "src/snapshot/embedded/embedded-data-inl.h"
14
15namespace v8 {
16namespace internal {
17
18namespace {
19
20int WriteDirectiveOrSeparator(PlatformEmbeddedFileWriterBase* w,
21                              int current_line_length,
22                              DataDirective directive) {
23  int printed_chars;
24  if (current_line_length == 0) {
25    printed_chars = w->IndentedDataDirective(directive);
26    DCHECK_LT(0, printed_chars);
27  } else {
28    printed_chars = fprintf(w->fp(), ",");
29    DCHECK_EQ(1, printed_chars);
30  }
31  return current_line_length + printed_chars;
32}
33
34int WriteLineEndIfNeeded(PlatformEmbeddedFileWriterBase* w,
35                         int current_line_length, int write_size) {
36  static const int kTextWidth = 100;
37  // Check if adding ',0xFF...FF\n"' would force a line wrap. This doesn't use
38  // the actual size of the string to be written to determine this so it's
39  // more conservative than strictly needed.
40  if (current_line_length + strlen(",0x") + write_size * 2 > kTextWidth) {
41    fprintf(w->fp(), "\n");
42    return 0;
43  } else {
44    return current_line_length;
45  }
46}
47
48}  // namespace
49
50void EmbeddedFileWriter::WriteBuiltin(PlatformEmbeddedFileWriterBase* w,
51                                      const i::EmbeddedData* blob,
52                                      const Builtin builtin) const {
53  const bool is_default_variant =
54      std::strcmp(embedded_variant_, kDefaultEmbeddedVariant) == 0;
55
56  base::EmbeddedVector<char, kTemporaryStringLength> builtin_symbol;
57  if (is_default_variant) {
58    // Create nicer symbol names for the default mode.
59    base::SNPrintF(builtin_symbol, "Builtins_%s", i::Builtins::name(builtin));
60  } else {
61    base::SNPrintF(builtin_symbol, "%s_Builtins_%s", embedded_variant_,
62                   i::Builtins::name(builtin));
63  }
64
65  // Labels created here will show up in backtraces. We check in
66  // Isolate::SetEmbeddedBlob that the blob layout remains unchanged, i.e.
67  // that labels do not insert bytes into the middle of the blob byte
68  // stream.
69  w->DeclareFunctionBegin(builtin_symbol.begin(),
70                          blob->InstructionSizeOfBuiltin(builtin));
71  const int builtin_id = static_cast<int>(builtin);
72  const std::vector<byte>& current_positions = source_positions_[builtin_id];
73  // The code below interleaves bytes of assembly code for the builtin
74  // function with source positions at the appropriate offsets.
75  base::Vector<const byte> vpos(current_positions.data(),
76                                current_positions.size());
77  v8::internal::SourcePositionTableIterator positions(
78      vpos, SourcePositionTableIterator::kExternalOnly);
79
80#ifndef DEBUG
81  CHECK(positions.done());  // Release builds must not contain debug infos.
82#endif
83
84  // Some builtins (JSConstructStubGeneric) have entry points located in the
85  // middle of them, we need to store their addresses since they are part of
86  // the list of allowed return addresses in the deoptimizer.
87  const std::vector<LabelInfo>& current_labels = label_info_[builtin_id];
88  auto label = current_labels.begin();
89
90  const uint8_t* data = reinterpret_cast<const uint8_t*>(
91      blob->InstructionStartOfBuiltin(builtin));
92  uint32_t size = blob->PaddedInstructionSizeOfBuiltin(builtin);
93  uint32_t i = 0;
94  uint32_t next_source_pos_offset =
95      static_cast<uint32_t>(positions.done() ? size : positions.code_offset());
96  uint32_t next_label_offset = static_cast<uint32_t>(
97      (label == current_labels.end()) ? size : label->offset);
98  uint32_t next_offset = 0;
99  while (i < size) {
100    if (i == next_source_pos_offset) {
101      // Write source directive.
102      w->SourceInfo(positions.source_position().ExternalFileId(),
103                    GetExternallyCompiledFilename(
104                        positions.source_position().ExternalFileId()),
105                    positions.source_position().ExternalLine());
106      positions.Advance();
107      next_source_pos_offset = static_cast<uint32_t>(
108          positions.done() ? size : positions.code_offset());
109      CHECK_GE(next_source_pos_offset, i);
110    }
111    if (i == next_label_offset) {
112      WriteBuiltinLabels(w, label->name);
113      label++;
114      next_label_offset = static_cast<uint32_t>(
115          (label == current_labels.end()) ? size : label->offset);
116      CHECK_GE(next_label_offset, i);
117    }
118    next_offset = std::min(next_source_pos_offset, next_label_offset);
119    WriteBinaryContentsAsInlineAssembly(w, data + i, next_offset - i);
120    i = next_offset;
121  }
122
123  w->DeclareFunctionEnd(builtin_symbol.begin());
124}
125
126void EmbeddedFileWriter::WriteBuiltinLabels(PlatformEmbeddedFileWriterBase* w,
127                                            std::string name) const {
128  if (ENABLE_CONTROL_FLOW_INTEGRITY_BOOL) {
129    w->DeclareSymbolGlobal(name.c_str());
130  }
131
132  w->DeclareLabel(name.c_str());
133}
134
135void EmbeddedFileWriter::WriteCodeSection(PlatformEmbeddedFileWriterBase* w,
136                                          const i::EmbeddedData* blob) const {
137  w->Comment(
138      "The embedded blob code section starts here. It contains the builtin");
139  w->Comment("instruction streams.");
140  w->SectionText();
141
142#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
143  // UMA needs an exposed function-type label at the start of the embedded
144  // code section.
145  static const char* kCodeStartForProfilerSymbolName =
146      "v8_code_start_for_profiler_";
147  static constexpr int kDummyFunctionLength = 1;
148  static constexpr int kDummyFunctionData = 0xcc;
149  w->DeclareFunctionBegin(kCodeStartForProfilerSymbolName,
150                          kDummyFunctionLength);
151  // The label must not be at the same address as the first builtin, insert
152  // padding bytes.
153  WriteDirectiveOrSeparator(w, 0, kByte);
154  w->HexLiteral(kDummyFunctionData);
155  w->Newline();
156  w->DeclareFunctionEnd(kCodeStartForProfilerSymbolName);
157#endif
158
159  w->AlignToCodeAlignment();
160  w->DeclareLabel(EmbeddedBlobCodeDataSymbol().c_str());
161
162  STATIC_ASSERT(Builtins::kAllBuiltinsAreIsolateIndependent);
163  for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
164       ++builtin) {
165    WriteBuiltin(w, blob, builtin);
166  }
167  w->PaddingAfterCode();
168  w->Newline();
169}
170
171void EmbeddedFileWriter::WriteFileEpilogue(PlatformEmbeddedFileWriterBase* w,
172                                           const i::EmbeddedData* blob) const {
173  {
174    base::EmbeddedVector<char, kTemporaryStringLength>
175        embedded_blob_code_symbol;
176    base::SNPrintF(embedded_blob_code_symbol, "v8_%s_embedded_blob_code_",
177                   embedded_variant_);
178
179    w->Comment("Pointer to the beginning of the embedded blob code.");
180    w->SectionData();
181    w->AlignToDataAlignment();
182    w->DeclarePointerToSymbol(embedded_blob_code_symbol.begin(),
183                              EmbeddedBlobCodeDataSymbol().c_str());
184    w->Newline();
185
186    base::EmbeddedVector<char, kTemporaryStringLength>
187        embedded_blob_data_symbol;
188    base::SNPrintF(embedded_blob_data_symbol, "v8_%s_embedded_blob_data_",
189                   embedded_variant_);
190
191    w->Comment("Pointer to the beginning of the embedded blob data section.");
192    w->AlignToDataAlignment();
193    w->DeclarePointerToSymbol(embedded_blob_data_symbol.begin(),
194                              EmbeddedBlobDataDataSymbol().c_str());
195    w->Newline();
196  }
197
198  {
199    base::EmbeddedVector<char, kTemporaryStringLength>
200        embedded_blob_code_size_symbol;
201    base::SNPrintF(embedded_blob_code_size_symbol,
202                   "v8_%s_embedded_blob_code_size_", embedded_variant_);
203
204    w->Comment("The size of the embedded blob code in bytes.");
205    w->SectionRoData();
206    w->AlignToDataAlignment();
207    w->DeclareUint32(embedded_blob_code_size_symbol.begin(), blob->code_size());
208    w->Newline();
209
210    base::EmbeddedVector<char, kTemporaryStringLength>
211        embedded_blob_data_size_symbol;
212    base::SNPrintF(embedded_blob_data_size_symbol,
213                   "v8_%s_embedded_blob_data_size_", embedded_variant_);
214
215    w->Comment("The size of the embedded blob data section in bytes.");
216    w->DeclareUint32(embedded_blob_data_size_symbol.begin(), blob->data_size());
217    w->Newline();
218  }
219
220#if defined(V8_OS_WIN64)
221  {
222    base::EmbeddedVector<char, kTemporaryStringLength> unwind_info_symbol;
223    base::SNPrintF(unwind_info_symbol, "%s_Builtins_UnwindInfo",
224                   embedded_variant_);
225
226    w->MaybeEmitUnwindData(unwind_info_symbol.begin(),
227                           EmbeddedBlobCodeDataSymbol().c_str(), blob,
228                           reinterpret_cast<const void*>(&unwind_infos_[0]));
229  }
230#endif  // V8_OS_WIN64
231
232  w->FileEpilogue();
233}
234
235// static
236void EmbeddedFileWriter::WriteBinaryContentsAsInlineAssembly(
237    PlatformEmbeddedFileWriterBase* w, const uint8_t* data, uint32_t size) {
238  int current_line_length = 0;
239  uint32_t i = 0;
240
241  // Begin by writing out byte chunks.
242  const DataDirective directive = w->ByteChunkDataDirective();
243  const int byte_chunk_size = DataDirectiveSize(directive);
244  for (; i + byte_chunk_size < size; i += byte_chunk_size) {
245    current_line_length =
246        WriteDirectiveOrSeparator(w, current_line_length, directive);
247    current_line_length += w->WriteByteChunk(data + i);
248    current_line_length =
249        WriteLineEndIfNeeded(w, current_line_length, byte_chunk_size);
250  }
251  if (current_line_length != 0) w->Newline();
252  current_line_length = 0;
253
254  // Write any trailing bytes one-by-one.
255  for (; i < size; i++) {
256    current_line_length =
257        WriteDirectiveOrSeparator(w, current_line_length, kByte);
258    current_line_length += w->HexLiteral(data[i]);
259    current_line_length = WriteLineEndIfNeeded(w, current_line_length, 1);
260  }
261
262  if (current_line_length != 0) w->Newline();
263}
264
265int EmbeddedFileWriter::LookupOrAddExternallyCompiledFilename(
266    const char* filename) {
267  auto result = external_filenames_.find(filename);
268  if (result != external_filenames_.end()) {
269    return result->second;
270  }
271  int new_id =
272      ExternalFilenameIndexToId(static_cast<int>(external_filenames_.size()));
273  external_filenames_.insert(std::make_pair(filename, new_id));
274  external_filenames_by_index_.push_back(filename);
275  DCHECK_EQ(external_filenames_by_index_.size(), external_filenames_.size());
276  return new_id;
277}
278
279const char* EmbeddedFileWriter::GetExternallyCompiledFilename(
280    int fileid) const {
281  size_t index = static_cast<size_t>(ExternalFilenameIdToIndex(fileid));
282  DCHECK_GE(index, 0);
283  DCHECK_LT(index, external_filenames_by_index_.size());
284
285  return external_filenames_by_index_[index];
286}
287
288int EmbeddedFileWriter::GetExternallyCompiledFilenameCount() const {
289  return static_cast<int>(external_filenames_.size());
290}
291
292void EmbeddedFileWriter::PrepareBuiltinSourcePositionMap(Builtins* builtins) {
293  for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
294       ++builtin) {
295    // Retrieve the SourcePositionTable and copy it.
296    Code code = FromCodeT(builtins->code(builtin));
297    // Verify that the code object is still the "real code" and not a
298    // trampoline (which wouldn't have source positions).
299    DCHECK(!code.is_off_heap_trampoline());
300    ByteArray source_position_table = code.source_position_table();
301    std::vector<unsigned char> data(source_position_table.GetDataStartAddress(),
302                                    source_position_table.GetDataEndAddress());
303    source_positions_[static_cast<int>(builtin)] = data;
304  }
305}
306
307void EmbeddedFileWriter::PrepareBuiltinLabelInfoMap(int create_offset,
308                                                    int invoke_offset) {
309  label_info_[static_cast<int>(Builtin::kJSConstructStubGeneric)].push_back(
310      {create_offset, "construct_stub_create_deopt_addr"});
311  label_info_[static_cast<int>(Builtin::kJSConstructStubGeneric)].push_back(
312      {invoke_offset, "construct_stub_invoke_deopt_addr"});
313}
314
315}  // namespace internal
316}  // namespace v8
317