Line data Source code
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_FILE_WRITER_H_
6 : #define V8_SNAPSHOT_EMBEDDED_FILE_WRITER_H_
7 :
8 : #include <cstdio>
9 : #include <cstring>
10 :
11 : #include "src/globals.h"
12 : #include "src/snapshot/snapshot.h"
13 : #include "src/source-position-table.h"
14 :
15 : #if defined(V8_OS_WIN_X64)
16 : #include "src/unwinding-info-win64.h"
17 : #endif
18 :
19 : namespace v8 {
20 : namespace internal {
21 :
22 : enum DataDirective {
23 : kByte,
24 : kLong,
25 : kQuad,
26 : kOcta,
27 : };
28 :
29 : static constexpr char kDefaultEmbeddedVariant[] = "Default";
30 :
31 : // The platform-dependent logic for emitting assembly code for the generated
32 : // embedded.S file.
33 : class EmbeddedFileWriter;
34 : class PlatformDependentEmbeddedFileWriter final {
35 : public:
36 1 : void SetFile(FILE* fp) { fp_ = fp; }
37 :
38 : void SectionText();
39 : void SectionData();
40 : void SectionRoData();
41 :
42 : void AlignToCodeAlignment();
43 : void AlignToDataAlignment();
44 :
45 : void DeclareUint32(const char* name, uint32_t value);
46 : void DeclarePointerToSymbol(const char* name, const char* target);
47 :
48 : #if defined(V8_OS_WIN_X64)
49 : void StartPdataSection();
50 : void EndPdataSection();
51 : void StartXdataSection();
52 : void EndXdataSection();
53 : void DeclareExternalFunction(const char* name);
54 :
55 : // Emits an RVA (address relative to the module load address) specified as an
56 : // offset from a given symbol.
57 : void DeclareRvaToSymbol(const char* name, uint64_t offset = 0);
58 : #endif
59 :
60 : void DeclareLabel(const char* name);
61 :
62 : void SourceInfo(int fileid, const char* filename, int line);
63 : void DeclareFunctionBegin(const char* name);
64 : void DeclareFunctionEnd(const char* name);
65 :
66 : // Returns the number of printed characters.
67 : int HexLiteral(uint64_t value);
68 :
69 : void Comment(const char* string);
70 4543 : void Newline() { fprintf(fp_, "\n"); }
71 :
72 : void FilePrologue();
73 : void DeclareExternalFilename(int fileid, const char* filename);
74 : void FileEpilogue();
75 :
76 : int IndentedDataDirective(DataDirective directive);
77 :
78 169813 : FILE* fp() const { return fp_; }
79 :
80 : private:
81 : void DeclareSymbolGlobal(const char* name);
82 :
83 : private:
84 : FILE* fp_ = nullptr;
85 : };
86 :
87 : // When writing out compiled builtins to a file, we
88 : // Detailed source-code information about builtins can only be obtained by
89 : // registration on the isolate during compilation.
90 1 : class EmbeddedFileWriterInterface {
91 : public:
92 : // We maintain a database of filenames to synthetic IDs.
93 : virtual int LookupOrAddExternallyCompiledFilename(const char* filename) = 0;
94 : virtual const char* GetExternallyCompiledFilename(int index) const = 0;
95 : virtual int GetExternallyCompiledFilenameCount() const = 0;
96 :
97 : // The isolate will call the method below just prior to replacing the
98 : // compiled builtin Code objects with trampolines.
99 : virtual void PrepareBuiltinSourcePositionMap(Builtins* builtins) = 0;
100 :
101 : #if defined(V8_OS_WIN_X64)
102 : virtual void SetBuiltinUnwindData(
103 : int builtin_index,
104 : const win64_unwindinfo::BuiltinUnwindInfo& unwinding_info) = 0;
105 : #endif
106 : };
107 :
108 : // Generates the embedded.S file which is later compiled into the final v8
109 : // binary. Its contents are exported through two symbols:
110 : //
111 : // v8_<variant>_embedded_blob_ (intptr_t):
112 : // a pointer to the start of the embedded blob.
113 : // v8_<variant>_embedded_blob_size_ (uint32_t):
114 : // size of the embedded blob in bytes.
115 : //
116 : // The variant is usually "Default" but can be modified in multisnapshot builds.
117 1526 : class EmbeddedFileWriter : public EmbeddedFileWriterInterface {
118 : public:
119 15214 : int LookupOrAddExternallyCompiledFilename(const char* filename) override {
120 : auto result = external_filenames_.find(filename);
121 15214 : if (result != external_filenames_.end()) {
122 15164 : return result->second;
123 : }
124 : int new_id =
125 50 : ExternalFilenameIndexToId(static_cast<int>(external_filenames_.size()));
126 100 : external_filenames_.insert(std::make_pair(filename, new_id));
127 50 : external_filenames_by_index_.push_back(filename);
128 : DCHECK_EQ(external_filenames_by_index_.size(), external_filenames_.size());
129 50 : return new_id;
130 : }
131 :
132 1108 : const char* GetExternallyCompiledFilename(int fileid) const override {
133 1108 : size_t index = static_cast<size_t>(ExternalFilenameIdToIndex(fileid));
134 : DCHECK_GE(index, 0);
135 : DCHECK_LT(index, external_filenames_by_index_.size());
136 :
137 1108 : return external_filenames_by_index_[index];
138 : }
139 :
140 0 : int GetExternallyCompiledFilenameCount() const override {
141 0 : return static_cast<int>(external_filenames_.size());
142 : }
143 :
144 : void PrepareBuiltinSourcePositionMap(Builtins* builtins) override;
145 :
146 : #if defined(V8_OS_WIN_X64)
147 : void SetBuiltinUnwindData(
148 : int builtin_index,
149 : const win64_unwindinfo::BuiltinUnwindInfo& unwinding_info) override;
150 : #endif
151 :
152 : void SetEmbeddedFile(const char* embedded_src_path) {
153 1 : embedded_src_path_ = embedded_src_path;
154 : }
155 :
156 : void SetEmbeddedVariant(const char* embedded_variant) {
157 1 : embedded_variant_ = embedded_variant;
158 : }
159 :
160 : void WriteEmbedded(const i::EmbeddedData* blob) const {
161 1 : MaybeWriteEmbeddedFile(blob);
162 : }
163 :
164 : private:
165 1 : void MaybeWriteEmbeddedFile(const i::EmbeddedData* blob) const {
166 1 : if (embedded_src_path_ == nullptr) return;
167 :
168 1 : FILE* fp = GetFileDescriptorOrDie(embedded_src_path_);
169 :
170 1 : PlatformDependentEmbeddedFileWriter writer;
171 : writer.SetFile(fp);
172 :
173 1 : WriteFilePrologue(&writer);
174 1 : WriteExternalFilenames(&writer);
175 1 : WriteMetadataSection(&writer, blob);
176 1 : WriteInstructionStreams(&writer, blob);
177 1 : WriteFileEpilogue(&writer, blob);
178 :
179 1 : fclose(fp);
180 : }
181 :
182 1 : static FILE* GetFileDescriptorOrDie(const char* filename) {
183 1 : FILE* fp = v8::base::OS::FOpen(filename, "wb");
184 1 : if (fp == nullptr) {
185 0 : i::PrintF("Unable to open file \"%s\" for writing.\n", filename);
186 0 : exit(1);
187 : }
188 1 : return fp;
189 : }
190 :
191 1 : void WriteFilePrologue(PlatformDependentEmbeddedFileWriter* w) const {
192 1 : w->Comment("Autogenerated file. Do not edit.");
193 : w->Newline();
194 1 : w->FilePrologue();
195 1 : }
196 :
197 1 : void WriteExternalFilenames(PlatformDependentEmbeddedFileWriter* w) const {
198 : w->Comment(
199 1 : "Source positions in the embedded blob refer to filenames by id.");
200 1 : w->Comment("Assembly directives here map the id to a filename.");
201 : w->Newline();
202 :
203 : // Write external filenames.
204 1 : int size = static_cast<int>(external_filenames_by_index_.size());
205 101 : for (int i = 0; i < size; i++) {
206 50 : w->DeclareExternalFilename(ExternalFilenameIndexToId(i),
207 100 : external_filenames_by_index_[i]);
208 : }
209 1 : }
210 :
211 : // Fairly arbitrary but should fit all symbol names.
212 : static constexpr int kTemporaryStringLength = 256;
213 :
214 : std::string EmbeddedBlobDataSymbol() const {
215 : char embedded_blob_data_symbol[kTemporaryStringLength];
216 2 : i::SNPrintF(i::Vector<char>(embedded_blob_data_symbol),
217 2 : "v8_%s_embedded_blob_data_", embedded_variant_);
218 2 : return embedded_blob_data_symbol;
219 : }
220 :
221 1 : void WriteMetadataSection(PlatformDependentEmbeddedFileWriter* w,
222 : const i::EmbeddedData* blob) const {
223 1 : w->Comment("The embedded blob starts here. Metadata comes first, followed");
224 1 : w->Comment("by builtin instruction streams.");
225 1 : w->SectionText();
226 1 : w->AlignToCodeAlignment();
227 2 : w->DeclareLabel(EmbeddedBlobDataSymbol().c_str());
228 :
229 : WriteBinaryContentsAsInlineAssembly(w, blob->data(),
230 1 : i::EmbeddedData::RawDataOffset());
231 1 : }
232 :
233 1521 : void WriteBuiltin(PlatformDependentEmbeddedFileWriter* w,
234 : const i::EmbeddedData* blob, const int builtin_id) const {
235 : const bool is_default_variant =
236 1521 : std::strcmp(embedded_variant_, kDefaultEmbeddedVariant) == 0;
237 :
238 : char builtin_symbol[kTemporaryStringLength];
239 1521 : if (is_default_variant) {
240 : // Create nicer symbol names for the default mode.
241 3042 : i::SNPrintF(i::Vector<char>(builtin_symbol), "Builtins_%s",
242 1521 : i::Builtins::name(builtin_id));
243 : } else {
244 0 : i::SNPrintF(i::Vector<char>(builtin_symbol), "%s_Builtins_%s",
245 0 : embedded_variant_, i::Builtins::name(builtin_id));
246 : }
247 :
248 : // Labels created here will show up in backtraces. We check in
249 : // Isolate::SetEmbeddedBlob that the blob layout remains unchanged, i.e.
250 : // that labels do not insert bytes into the middle of the blob byte
251 : // stream.
252 1521 : w->DeclareFunctionBegin(builtin_symbol);
253 1521 : const std::vector<byte>& current_positions = source_positions_[builtin_id];
254 :
255 : // The code below interleaves bytes of assembly code for the builtin
256 : // function with source positions at the appropriate offsets.
257 : Vector<const byte> vpos(current_positions.data(), current_positions.size());
258 : v8::internal::SourcePositionTableIterator positions(
259 1521 : vpos, SourcePositionTableIterator::kExternalOnly);
260 :
261 : const uint8_t* data = reinterpret_cast<const uint8_t*>(
262 1521 : blob->InstructionStartOfBuiltin(builtin_id));
263 : uint32_t size = blob->PaddedInstructionSizeOfBuiltin(builtin_id);
264 : uint32_t i = 0;
265 : uint32_t next_offset = static_cast<uint32_t>(
266 1521 : positions.done() ? size : positions.code_offset());
267 6779 : while (i < size) {
268 2629 : if (i == next_offset) {
269 : // Write source directive.
270 1108 : w->SourceInfo(positions.source_position().ExternalFileId(),
271 : GetExternallyCompiledFilename(
272 1108 : positions.source_position().ExternalFileId()),
273 1108 : positions.source_position().ExternalLine());
274 1108 : positions.Advance();
275 : next_offset = static_cast<uint32_t>(
276 1108 : positions.done() ? size : positions.code_offset());
277 : }
278 2629 : CHECK_GE(next_offset, i);
279 2629 : WriteBinaryContentsAsInlineAssembly(w, data + i, next_offset - i);
280 : i = next_offset;
281 : }
282 :
283 1521 : w->DeclareFunctionEnd(builtin_symbol);
284 1521 : }
285 :
286 1 : void WriteInstructionStreams(PlatformDependentEmbeddedFileWriter* w,
287 : const i::EmbeddedData* blob) const {
288 3043 : for (int i = 0; i < i::Builtins::builtin_count; i++) {
289 1521 : if (!blob->ContainsBuiltin(i)) continue;
290 :
291 1521 : WriteBuiltin(w, blob, i);
292 : }
293 : w->Newline();
294 1 : }
295 :
296 1 : void WriteFileEpilogue(PlatformDependentEmbeddedFileWriter* w,
297 : const i::EmbeddedData* blob) const {
298 : {
299 : char embedded_blob_symbol[kTemporaryStringLength];
300 1 : i::SNPrintF(i::Vector<char>(embedded_blob_symbol), "v8_%s_embedded_blob_",
301 2 : embedded_variant_);
302 :
303 1 : w->Comment("Pointer to the beginning of the embedded blob.");
304 1 : w->SectionData();
305 1 : w->AlignToDataAlignment();
306 : w->DeclarePointerToSymbol(embedded_blob_symbol,
307 2 : EmbeddedBlobDataSymbol().c_str());
308 : w->Newline();
309 : }
310 :
311 : {
312 : char embedded_blob_size_symbol[kTemporaryStringLength];
313 1 : i::SNPrintF(i::Vector<char>(embedded_blob_size_symbol),
314 2 : "v8_%s_embedded_blob_size_", embedded_variant_);
315 :
316 1 : w->Comment("The size of the embedded blob in bytes.");
317 1 : w->SectionRoData();
318 1 : w->AlignToDataAlignment();
319 1 : w->DeclareUint32(embedded_blob_size_symbol, blob->size());
320 : w->Newline();
321 : }
322 :
323 : #if defined(V8_OS_WIN_X64)
324 : if (win64_unwindinfo::CanEmitUnwindInfoForBuiltins()) {
325 : WriteUnwindInfo(w, blob);
326 : }
327 : #endif
328 :
329 1 : w->FileEpilogue();
330 1 : }
331 :
332 : #if defined(V8_OS_WIN_X64)
333 : std::string BuiltinsUnwindInfoLabel() const;
334 : void WriteUnwindInfo(PlatformDependentEmbeddedFileWriter* w,
335 : const i::EmbeddedData* blob) const;
336 : void WriteUnwindInfoEntry(PlatformDependentEmbeddedFileWriter* w,
337 : uint64_t rva_start, uint64_t rva_end) const;
338 : #endif
339 :
340 : #if defined(_MSC_VER) && !defined(__clang__)
341 : #define V8_COMPILER_IS_MSVC
342 : #endif
343 :
344 : #if defined(V8_COMPILER_IS_MSVC)
345 : // Windows MASM doesn't have an .octa directive, use QWORDs instead.
346 : // Note: MASM *really* does not like large data streams. It takes over 5
347 : // minutes to assemble the ~350K lines of embedded.S produced when using
348 : // BYTE directives in a debug build. QWORD produces roughly 120KLOC and
349 : // reduces assembly time to ~40 seconds. Still terrible, but much better
350 : // than before. See also: https://crbug.com/v8/8475.
351 : static constexpr DataDirective kByteChunkDirective = kQuad;
352 : static constexpr int kByteChunkSize = 8;
353 :
354 : static int WriteByteChunk(PlatformDependentEmbeddedFileWriter* w,
355 : int current_line_length, const uint8_t* data) {
356 : const uint64_t* quad_ptr = reinterpret_cast<const uint64_t*>(data);
357 : return current_line_length + w->HexLiteral(*quad_ptr);
358 : }
359 :
360 : #elif defined(V8_OS_AIX)
361 : // PPC uses a fixed 4 byte instruction set, using .long
362 : // to prevent any unnecessary padding.
363 : static constexpr DataDirective kByteChunkDirective = kLong;
364 : static constexpr int kByteChunkSize = 4;
365 :
366 : static int WriteByteChunk(PlatformDependentEmbeddedFileWriter* w,
367 : int current_line_length, const uint8_t* data) {
368 : const uint32_t* long_ptr = reinterpret_cast<const uint32_t*>(data);
369 : return current_line_length + w->HexLiteral(*long_ptr);
370 : }
371 :
372 : #else // defined(V8_COMPILER_IS_MSVC) || defined(V8_OS_AIX)
373 : static constexpr DataDirective kByteChunkDirective = kOcta;
374 : static constexpr int kByteChunkSize = 16;
375 :
376 71087 : static int WriteByteChunk(PlatformDependentEmbeddedFileWriter* w,
377 : int current_line_length, const uint8_t* data) {
378 : const size_t size = kInt64Size;
379 :
380 : uint64_t part1, part2;
381 : // Use memcpy for the reads since {data} is not guaranteed to be aligned.
382 : #ifdef V8_TARGET_BIG_ENDIAN
383 : memcpy(&part1, data, size);
384 : memcpy(&part2, data + size, size);
385 : #else
386 : memcpy(&part1, data + size, size);
387 : memcpy(&part2, data, size);
388 : #endif // V8_TARGET_BIG_ENDIAN
389 :
390 71087 : if (part1 != 0) {
391 : current_line_length +=
392 71087 : fprintf(w->fp(), "0x%" PRIx64 "%016" PRIx64, part1, part2);
393 : } else {
394 0 : current_line_length += fprintf(w->fp(), "0x%" PRIx64, part2);
395 : }
396 71087 : return current_line_length;
397 : }
398 : #endif // defined(V8_COMPILER_IS_MSVC) || defined(V8_OS_AIX)
399 : #undef V8_COMPILER_IS_MSVC
400 :
401 103263 : static int WriteDirectiveOrSeparator(PlatformDependentEmbeddedFileWriter* w,
402 : int current_line_length,
403 : DataDirective directive) {
404 : int printed_chars;
405 103263 : if (current_line_length == 0) {
406 39005 : printed_chars = w->IndentedDataDirective(directive);
407 : DCHECK_LT(0, printed_chars);
408 : } else {
409 : printed_chars = fprintf(w->fp(), ",");
410 : DCHECK_EQ(1, printed_chars);
411 : }
412 103263 : return current_line_length + printed_chars;
413 : }
414 :
415 : static int WriteLineEndIfNeeded(PlatformDependentEmbeddedFileWriter* w,
416 : int current_line_length, int write_size) {
417 : static const int kTextWidth = 100;
418 : // Check if adding ',0xFF...FF\n"' would force a line wrap. This doesn't use
419 : // the actual size of the string to be written to determine this so it's
420 : // more conservative than strictly needed.
421 103263 : if (current_line_length + strlen(",0x") + write_size * 2 > kTextWidth) {
422 : fprintf(w->fp(), "\n");
423 : return 0;
424 : } else {
425 : return current_line_length;
426 : }
427 : }
428 :
429 2630 : static void WriteBinaryContentsAsInlineAssembly(
430 : PlatformDependentEmbeddedFileWriter* w, const uint8_t* data,
431 : uint32_t size) {
432 : int current_line_length = 0;
433 : uint32_t i = 0;
434 :
435 : // Begin by writing out byte chunks.
436 144804 : for (; i + kByteChunkSize < size; i += kByteChunkSize) {
437 : current_line_length = WriteDirectiveOrSeparator(w, current_line_length,
438 71087 : kByteChunkDirective);
439 71087 : current_line_length = WriteByteChunk(w, current_line_length, data + i);
440 : current_line_length =
441 : WriteLineEndIfNeeded(w, current_line_length, kByteChunkSize);
442 : }
443 2630 : if (current_line_length != 0) w->Newline();
444 : current_line_length = 0;
445 :
446 : // Write any trailing bytes one-by-one.
447 66982 : for (; i < size; i++) {
448 : current_line_length =
449 32176 : WriteDirectiveOrSeparator(w, current_line_length, kByte);
450 32176 : current_line_length += w->HexLiteral(data[i]);
451 : current_line_length = WriteLineEndIfNeeded(w, current_line_length, 1);
452 : }
453 :
454 2630 : if (current_line_length != 0) w->Newline();
455 2630 : }
456 :
457 : static int ExternalFilenameIndexToId(int index) {
458 100 : return kFirstExternalFilenameId + index;
459 : }
460 :
461 : static int ExternalFilenameIdToIndex(int id) {
462 1108 : return id - kFirstExternalFilenameId;
463 : }
464 :
465 : std::vector<byte> source_positions_[Builtins::builtin_count];
466 :
467 : #if defined(V8_OS_WIN_X64)
468 : win64_unwindinfo::BuiltinUnwindInfo unwind_infos_[Builtins::builtin_count];
469 : #endif
470 :
471 : // In assembly directives, filename ids need to begin with 1.
472 : static const int kFirstExternalFilenameId = 1;
473 : std::map<const char*, int> external_filenames_;
474 : std::vector<const char*> external_filenames_by_index_;
475 :
476 : const char* embedded_src_path_ = nullptr;
477 : const char* embedded_variant_ = kDefaultEmbeddedVariant;
478 : };
479 :
480 : } // namespace internal
481 : } // namespace v8
482 :
483 : #endif // V8_SNAPSHOT_EMBEDDED_FILE_WRITER_H_
|