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