| // Copyright 2018 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/snapshot/embedded/embedded-file-writer.h" |
| |
| #include <algorithm> |
| #include <cinttypes> |
| |
| #include "src/codegen/source-position-table.h" |
| #include "src/flags/flags.h" // For ENABLE_CONTROL_FLOW_INTEGRITY_BOOL |
| #include "src/objects/code-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| namespace { |
| |
| int WriteDirectiveOrSeparator(PlatformEmbeddedFileWriterBase* w, |
| int current_line_length, |
| DataDirective directive) { |
| int printed_chars; |
| if (current_line_length == 0) { |
| printed_chars = w->IndentedDataDirective(directive); |
| DCHECK_LT(0, printed_chars); |
| } else { |
| printed_chars = fprintf(w->fp(), ","); |
| DCHECK_EQ(1, printed_chars); |
| } |
| return current_line_length + printed_chars; |
| } |
| |
| int WriteLineEndIfNeeded(PlatformEmbeddedFileWriterBase* w, |
| int current_line_length, int write_size) { |
| static const int kTextWidth = 100; |
| // Check if adding ',0xFF...FF\n"' would force a line wrap. This doesn't use |
| // the actual size of the string to be written to determine this so it's |
| // more conservative than strictly needed. |
| if (current_line_length + strlen(",0x") + write_size * 2 > kTextWidth) { |
| fprintf(w->fp(), "\n"); |
| return 0; |
| } else { |
| return current_line_length; |
| } |
| } |
| |
| } // namespace |
| |
| void EmbeddedFileWriter::WriteBuiltin(PlatformEmbeddedFileWriterBase* w, |
| const i::EmbeddedData* blob, |
| const int builtin_id) const { |
| const bool is_default_variant = |
| std::strcmp(embedded_variant_, kDefaultEmbeddedVariant) == 0; |
| |
| i::EmbeddedVector<char, kTemporaryStringLength> builtin_symbol; |
| if (is_default_variant) { |
| // Create nicer symbol names for the default mode. |
| i::SNPrintF(builtin_symbol, "Builtins_%s", i::Builtins::name(builtin_id)); |
| } else { |
| i::SNPrintF(builtin_symbol, "%s_Builtins_%s", embedded_variant_, |
| i::Builtins::name(builtin_id)); |
| } |
| |
| // Labels created here will show up in backtraces. We check in |
| // Isolate::SetEmbeddedBlob that the blob layout remains unchanged, i.e. |
| // that labels do not insert bytes into the middle of the blob byte |
| // stream. |
| w->DeclareFunctionBegin(builtin_symbol.begin(), |
| blob->InstructionSizeOfBuiltin(builtin_id)); |
| const std::vector<byte>& current_positions = source_positions_[builtin_id]; |
| // The code below interleaves bytes of assembly code for the builtin |
| // function with source positions at the appropriate offsets. |
| Vector<const byte> vpos(current_positions.data(), current_positions.size()); |
| v8::internal::SourcePositionTableIterator positions( |
| vpos, SourcePositionTableIterator::kExternalOnly); |
| |
| #ifndef DEBUG |
| CHECK(positions.done()); // Release builds must not contain debug infos. |
| #endif |
| |
| // Some builtins (ArgumentsAdaptorTrampoline and JSConstructStubGeneric) have |
| // entry points located in the middle of them, we need to store their |
| // addresses since they are part of the list of allowed return addresses in |
| // the deoptimizer. |
| const std::vector<LabelInfo>& current_labels = label_info_[builtin_id]; |
| auto label = current_labels.begin(); |
| |
| const uint8_t* data = reinterpret_cast<const uint8_t*>( |
| blob->InstructionStartOfBuiltin(builtin_id)); |
| uint32_t size = blob->PaddedInstructionSizeOfBuiltin(builtin_id); |
| uint32_t i = 0; |
| uint32_t next_source_pos_offset = |
| static_cast<uint32_t>(positions.done() ? size : positions.code_offset()); |
| uint32_t next_label_offset = static_cast<uint32_t>( |
| (label == current_labels.end()) ? size : label->offset); |
| uint32_t next_offset = 0; |
| while (i < size) { |
| if (i == next_source_pos_offset) { |
| // Write source directive. |
| w->SourceInfo(positions.source_position().ExternalFileId(), |
| GetExternallyCompiledFilename( |
| positions.source_position().ExternalFileId()), |
| positions.source_position().ExternalLine()); |
| positions.Advance(); |
| next_source_pos_offset = static_cast<uint32_t>( |
| positions.done() ? size : positions.code_offset()); |
| CHECK_GE(next_source_pos_offset, i); |
| } |
| if (i == next_label_offset) { |
| WriteBuiltinLabels(w, label->name); |
| label++; |
| next_label_offset = static_cast<uint32_t>( |
| (label == current_labels.end()) ? size : label->offset); |
| CHECK_GE(next_label_offset, i); |
| } |
| next_offset = std::min(next_source_pos_offset, next_label_offset); |
| WriteBinaryContentsAsInlineAssembly(w, data + i, next_offset - i); |
| i = next_offset; |
| } |
| |
| w->DeclareFunctionEnd(builtin_symbol.begin()); |
| } |
| |
| void EmbeddedFileWriter::WriteBuiltinLabels(PlatformEmbeddedFileWriterBase* w, |
| std::string name) const { |
| if (ENABLE_CONTROL_FLOW_INTEGRITY_BOOL) { |
| w->DeclareSymbolGlobal(name.c_str()); |
| } |
| |
| w->DeclareLabel(name.c_str()); |
| } |
| |
| void EmbeddedFileWriter::WriteCodeSection(PlatformEmbeddedFileWriterBase* w, |
| const i::EmbeddedData* blob) const { |
| w->Comment( |
| "The embedded blob code section starts here. It contains the builtin"); |
| w->Comment("instruction streams."); |
| w->SectionText(); |
| |
| #if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 |
| // UMA needs an exposed function-type label at the start of the embedded |
| // code section. |
| static const char* kCodeStartForProfilerSymbolName = |
| "v8_code_start_for_profiler_"; |
| static constexpr int kDummyFunctionLength = 1; |
| static constexpr int kDummyFunctionData = 0xcc; |
| w->DeclareFunctionBegin(kCodeStartForProfilerSymbolName, |
| kDummyFunctionLength); |
| // The label must not be at the same address as the first builtin, insert |
| // padding bytes. |
| WriteDirectiveOrSeparator(w, 0, kByte); |
| w->HexLiteral(kDummyFunctionData); |
| w->Newline(); |
| w->DeclareFunctionEnd(kCodeStartForProfilerSymbolName); |
| #endif |
| |
| w->AlignToCodeAlignment(); |
| w->DeclareLabel(EmbeddedBlobCodeDataSymbol().c_str()); |
| |
| STATIC_ASSERT(Builtins::kAllBuiltinsAreIsolateIndependent); |
| for (int i = 0; i < i::Builtins::builtin_count; i++) { |
| WriteBuiltin(w, blob, i); |
| } |
| w->Newline(); |
| } |
| |
| void EmbeddedFileWriter::WriteFileEpilogue(PlatformEmbeddedFileWriterBase* w, |
| const i::EmbeddedData* blob) const { |
| { |
| i::EmbeddedVector<char, kTemporaryStringLength> embedded_blob_code_symbol; |
| i::SNPrintF(embedded_blob_code_symbol, "v8_%s_embedded_blob_code_", |
| embedded_variant_); |
| |
| w->Comment("Pointer to the beginning of the embedded blob code."); |
| w->SectionData(); |
| w->AlignToDataAlignment(); |
| w->DeclarePointerToSymbol(embedded_blob_code_symbol.begin(), |
| EmbeddedBlobCodeDataSymbol().c_str()); |
| w->Newline(); |
| |
| i::EmbeddedVector<char, kTemporaryStringLength> embedded_blob_data_symbol; |
| i::SNPrintF(embedded_blob_data_symbol, "v8_%s_embedded_blob_data_", |
| embedded_variant_); |
| |
| w->Comment("Pointer to the beginning of the embedded blob data section."); |
| w->AlignToDataAlignment(); |
| w->DeclarePointerToSymbol(embedded_blob_data_symbol.begin(), |
| EmbeddedBlobDataDataSymbol().c_str()); |
| w->Newline(); |
| } |
| |
| { |
| i::EmbeddedVector<char, kTemporaryStringLength> |
| embedded_blob_code_size_symbol; |
| i::SNPrintF(embedded_blob_code_size_symbol, |
| "v8_%s_embedded_blob_code_size_", embedded_variant_); |
| |
| w->Comment("The size of the embedded blob code in bytes."); |
| w->SectionRoData(); |
| w->AlignToDataAlignment(); |
| w->DeclareUint32(embedded_blob_code_size_symbol.begin(), blob->code_size()); |
| w->Newline(); |
| |
| i::EmbeddedVector<char, kTemporaryStringLength> |
| embedded_blob_data_size_symbol; |
| i::SNPrintF(embedded_blob_data_size_symbol, |
| "v8_%s_embedded_blob_data_size_", embedded_variant_); |
| |
| w->Comment("The size of the embedded blob data section in bytes."); |
| w->DeclareUint32(embedded_blob_data_size_symbol.begin(), blob->data_size()); |
| w->Newline(); |
| } |
| |
| #if defined(V8_OS_WIN64) |
| { |
| i::EmbeddedVector<char, kTemporaryStringLength> unwind_info_symbol; |
| i::SNPrintF(unwind_info_symbol, "%s_Builtins_UnwindInfo", |
| embedded_variant_); |
| |
| w->MaybeEmitUnwindData(unwind_info_symbol.begin(), |
| EmbeddedBlobCodeDataSymbol().c_str(), blob, |
| reinterpret_cast<const void*>(&unwind_infos_[0])); |
| } |
| #endif // V8_OS_WIN64 |
| |
| w->FileEpilogue(); |
| } |
| |
| // static |
| void EmbeddedFileWriter::WriteBinaryContentsAsInlineAssembly( |
| PlatformEmbeddedFileWriterBase* w, const uint8_t* data, uint32_t size) { |
| int current_line_length = 0; |
| uint32_t i = 0; |
| |
| // Begin by writing out byte chunks. |
| const DataDirective directive = w->ByteChunkDataDirective(); |
| const int byte_chunk_size = DataDirectiveSize(directive); |
| for (; i + byte_chunk_size < size; i += byte_chunk_size) { |
| current_line_length = |
| WriteDirectiveOrSeparator(w, current_line_length, directive); |
| current_line_length += w->WriteByteChunk(data + i); |
| current_line_length = |
| WriteLineEndIfNeeded(w, current_line_length, byte_chunk_size); |
| } |
| if (current_line_length != 0) w->Newline(); |
| current_line_length = 0; |
| |
| // Write any trailing bytes one-by-one. |
| for (; i < size; i++) { |
| current_line_length = |
| WriteDirectiveOrSeparator(w, current_line_length, kByte); |
| current_line_length += w->HexLiteral(data[i]); |
| current_line_length = WriteLineEndIfNeeded(w, current_line_length, 1); |
| } |
| |
| if (current_line_length != 0) w->Newline(); |
| } |
| |
| int EmbeddedFileWriter::LookupOrAddExternallyCompiledFilename( |
| const char* filename) { |
| auto result = external_filenames_.find(filename); |
| if (result != external_filenames_.end()) { |
| return result->second; |
| } |
| int new_id = |
| ExternalFilenameIndexToId(static_cast<int>(external_filenames_.size())); |
| external_filenames_.insert(std::make_pair(filename, new_id)); |
| external_filenames_by_index_.push_back(filename); |
| DCHECK_EQ(external_filenames_by_index_.size(), external_filenames_.size()); |
| return new_id; |
| } |
| |
| const char* EmbeddedFileWriter::GetExternallyCompiledFilename( |
| int fileid) const { |
| size_t index = static_cast<size_t>(ExternalFilenameIdToIndex(fileid)); |
| DCHECK_GE(index, 0); |
| DCHECK_LT(index, external_filenames_by_index_.size()); |
| |
| return external_filenames_by_index_[index]; |
| } |
| |
| int EmbeddedFileWriter::GetExternallyCompiledFilenameCount() const { |
| return static_cast<int>(external_filenames_.size()); |
| } |
| |
| void EmbeddedFileWriter::PrepareBuiltinSourcePositionMap(Builtins* builtins) { |
| for (int i = 0; i < Builtins::builtin_count; i++) { |
| // Retrieve the SourcePositionTable and copy it. |
| Code code = builtins->builtin(i); |
| // Verify that the code object is still the "real code" and not a |
| // trampoline (which wouldn't have source positions). |
| DCHECK(!code.is_off_heap_trampoline()); |
| std::vector<unsigned char> data( |
| code.SourcePositionTable().GetDataStartAddress(), |
| code.SourcePositionTable().GetDataEndAddress()); |
| source_positions_[i] = data; |
| } |
| } |
| |
| void EmbeddedFileWriter::PrepareBuiltinLabelInfoMap( |
| int create_offset, int invoke_offset, int arguments_adaptor_offset) { |
| label_info_[Builtins::kJSConstructStubGeneric].push_back( |
| {create_offset, "construct_stub_create_deopt_addr"}); |
| label_info_[Builtins::kJSConstructStubGeneric].push_back( |
| {invoke_offset, "construct_stub_invoke_deopt_addr"}); |
| label_info_[Builtins::kArgumentsAdaptorTrampoline].push_back( |
| {arguments_adaptor_offset, "arguments_adaptor_deopt_addr"}); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |