blob: b472841cc65f7a759a5ba2b6e59a0ab1b0e7dcf6 [file] [log] [blame]
// 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