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
15 namespace v8 {
16 namespace internal {
17
18 namespace {
19
WriteDirectiveOrSeparator(PlatformEmbeddedFileWriterBase * w,int current_line_length,DataDirective directive)20 int 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
WriteLineEndIfNeeded(PlatformEmbeddedFileWriterBase * w,int current_line_length,int write_size)34 int 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
WriteBuiltin(PlatformEmbeddedFileWriterBase * w,const i::EmbeddedData * blob,const Builtin builtin) const50 void 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
WriteBuiltinLabels(PlatformEmbeddedFileWriterBase * w,std::string name) const126 void 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
WriteCodeSection(PlatformEmbeddedFileWriterBase * w,const i::EmbeddedData * blob) const135 void 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
WriteFileEpilogue(PlatformEmbeddedFileWriterBase * w,const i::EmbeddedData * blob) const171 void 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
WriteBinaryContentsAsInlineAssembly(PlatformEmbeddedFileWriterBase * w,const uint8_t * data,uint32_t size)236 void 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
LookupOrAddExternallyCompiledFilename(const char * filename)265 int 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
GetExternallyCompiledFilename(int fileid) const279 const 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
GetExternallyCompiledFilenameCount() const288 int EmbeddedFileWriter::GetExternallyCompiledFilenameCount() const {
289 return static_cast<int>(external_filenames_.size());
290 }
291
PrepareBuiltinSourcePositionMap(Builtins * builtins)292 void 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
PrepareBuiltinLabelInfoMap(int create_offset,int invoke_offset)307 void 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