mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:59:38 +00:00
b9b6511ca6
Closes https://github.com/dart-lang/sdk/pull/50918 Co-authored-by: Josh Soref <jsoref@gmail.com> GitOrigin-RevId: 1fd275051c561b63d374fb47e76a22424c4a12a9 Change-Id: I97790d9c79ff659f2c1fa2d2d46d041fe67957cb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278530 Reviewed-by: William Hesse <whesse@google.com> Commit-Queue: Alexander Thomas <athom@google.com> Reviewed-by: Tess Strickland <sstrickl@google.com>
764 lines
28 KiB
C++
764 lines
28 KiB
C++
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_IMAGE_SNAPSHOT_H_
|
|
#define RUNTIME_VM_IMAGE_SNAPSHOT_H_
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "platform/assert.h"
|
|
#include "platform/utils.h"
|
|
#include "vm/allocation.h"
|
|
#include "vm/compiler/runtime_api.h"
|
|
#include "vm/datastream.h"
|
|
#include "vm/elf.h"
|
|
#include "vm/globals.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/hash_map.h"
|
|
#include "vm/object.h"
|
|
#include "vm/reusable_handles.h"
|
|
#include "vm/type_testing_stubs.h"
|
|
#include "vm/v8_snapshot_writer.h"
|
|
|
|
#if defined(DEBUG)
|
|
#define SNAPSHOT_BACKTRACE
|
|
#endif
|
|
|
|
namespace dart {
|
|
|
|
// Forward declarations.
|
|
class BitsContainer;
|
|
class Code;
|
|
class Dwarf;
|
|
class Elf;
|
|
class Instructions;
|
|
class Object;
|
|
|
|
class Image : ValueObject {
|
|
public:
|
|
explicit Image(const void* raw_memory)
|
|
: Image(reinterpret_cast<uword>(raw_memory)) {}
|
|
explicit Image(const uword raw_memory)
|
|
: raw_memory_(raw_memory),
|
|
snapshot_size_(FieldValue(raw_memory, HeaderField::ImageSize)),
|
|
extra_info_(ExtraInfo(raw_memory_, snapshot_size_)) {
|
|
ASSERT(Utils::IsAligned(raw_memory, kObjectStartAlignment));
|
|
}
|
|
|
|
// Even though an Image is read-only memory, we must return a void* here.
|
|
// All objects in an Image are pre-marked, though, so the GC will not attempt
|
|
// to change the returned memory.
|
|
void* object_start() const {
|
|
return reinterpret_cast<void*>(raw_memory_ + kHeaderSize);
|
|
}
|
|
|
|
uword object_size() const { return snapshot_size_ - kHeaderSize; }
|
|
|
|
bool contains(uword address) const {
|
|
uword start = reinterpret_cast<uword>(object_start());
|
|
return address >= start && (address - start < object_size());
|
|
}
|
|
|
|
// Returns the address of the BSS section, or nullptr if one is not available.
|
|
// Only has meaning for instructions images from precompiled snapshots.
|
|
uword* bss() const;
|
|
|
|
// Returns the relocated address of the isolate's instructions, or 0 if
|
|
// one is not available. Only has meaning for instructions images from
|
|
// precompiled snapshots.
|
|
uword instructions_relocated_address() const;
|
|
|
|
// Returns the GNU build ID, or nullptr if not available. See
|
|
// build_id_length() for the length of the returned buffer. Only has meaning
|
|
// for instructions images from precompiled snapshots.
|
|
const uint8_t* build_id() const;
|
|
|
|
// Returns the length of the GNU build ID returned by build_id(). Only has
|
|
// meaning for instructions images from precompiled snapshots.
|
|
intptr_t build_id_length() const;
|
|
|
|
// Returns whether this instructions section was compiled to ELF. Only has
|
|
// meaning for instructions images from precompiled snapshots.
|
|
bool compiled_to_elf() const;
|
|
|
|
private:
|
|
// Word-sized fields in an Image object header.
|
|
enum class HeaderField : intptr_t {
|
|
// The size of the image (total of header and payload).
|
|
ImageSize,
|
|
// The offset of the InstructionsSection object in the image. Note this
|
|
// offset is from the start of the _image_, _not_ from its payload start,
|
|
// so we can detect images without an InstructionsSection by a 0 value here.
|
|
InstructionsSectionOffset,
|
|
// If adding more fields, updating kHeaderFields below. (However, more
|
|
// fields _can't_ be added on 64-bit architectures, see the restrictions
|
|
// on kHeaderSize below.)
|
|
};
|
|
|
|
// Number of fields described by the HeaderField enum.
|
|
static constexpr intptr_t kHeaderFields =
|
|
static_cast<intptr_t>(HeaderField::InstructionsSectionOffset) + 1;
|
|
|
|
static uword FieldValue(uword raw_memory, HeaderField field) {
|
|
return reinterpret_cast<const uword*>(
|
|
raw_memory)[static_cast<intptr_t>(field)];
|
|
}
|
|
|
|
// Constants used to denote special values for the offsets in the Image
|
|
// object header and the fields of the InstructionsSection object.
|
|
static constexpr intptr_t kNoInstructionsSection = 0;
|
|
static constexpr intptr_t kNoBssSection = 0;
|
|
static constexpr intptr_t kNoRelocatedAddress = 0;
|
|
static constexpr intptr_t kNoBuildId = 0;
|
|
|
|
// The size of the Image object header.
|
|
//
|
|
// Note: Image::kHeaderSize is _not_ an architecture-dependent constant,
|
|
// and so there is no compiler::target::Image::kHeaderSize.
|
|
static constexpr intptr_t kHeaderSize = kObjectStartAlignment;
|
|
// Explicitly double-checking kHeaderSize is never changed. Increasing the
|
|
// Image header size would mean objects would not start at a place expected
|
|
// by parts of the VM (like the GC) that use Image pages as Pages.
|
|
static_assert(kHeaderSize == kObjectStartAlignment,
|
|
"Image page cannot be used as Page");
|
|
// Make sure that the number of fields in the Image header fit both on the
|
|
// host and target architectures.
|
|
static_assert(kHeaderFields * kWordSize <= kHeaderSize,
|
|
"Too many fields in Image header for host architecture");
|
|
static_assert(kHeaderFields * compiler::target::kWordSize <= kHeaderSize,
|
|
"Too many fields in Image header for target architecture");
|
|
|
|
// We don't use a handle or the tagged pointer because this object cannot be
|
|
// moved in memory by the GC.
|
|
static const UntaggedInstructionsSection* ExtraInfo(const uword raw_memory,
|
|
const uword size);
|
|
|
|
// Most internal uses would cast this to uword, so just store it as such.
|
|
const uword raw_memory_;
|
|
const intptr_t snapshot_size_;
|
|
const UntaggedInstructionsSection* const extra_info_;
|
|
|
|
// For access to private constants.
|
|
friend class AssemblyImageWriter;
|
|
friend class BitsContainer;
|
|
friend class BlobImageWriter;
|
|
friend class ImageWriter;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Image);
|
|
};
|
|
|
|
class ImageReader : public ZoneAllocated {
|
|
public:
|
|
ImageReader(const uint8_t* data_image, const uint8_t* instructions_image);
|
|
|
|
ApiErrorPtr VerifyAlignment() const;
|
|
|
|
ONLY_IN_PRECOMPILED(uword GetBareInstructionsAt(uint32_t offset) const);
|
|
ONLY_IN_PRECOMPILED(uword GetBareInstructionsEnd() const);
|
|
InstructionsPtr GetInstructionsAt(uint32_t offset) const;
|
|
ObjectPtr GetObjectAt(uint32_t offset) const;
|
|
|
|
private:
|
|
const uint8_t* data_image_;
|
|
const uint8_t* instructions_image_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ImageReader);
|
|
};
|
|
|
|
struct ObjectOffsetPair {
|
|
public:
|
|
ObjectOffsetPair() : ObjectOffsetPair(NULL, 0) {}
|
|
ObjectOffsetPair(ObjectPtr obj, int32_t off) : object(obj), offset(off) {}
|
|
|
|
ObjectPtr object;
|
|
int32_t offset;
|
|
};
|
|
|
|
class ObjectOffsetTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef ObjectPtr Key;
|
|
typedef int32_t Value;
|
|
typedef ObjectOffsetPair Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv.object; }
|
|
static Value ValueOf(Pair kv) { return kv.offset; }
|
|
static uword Hash(Key key);
|
|
static inline bool IsKeyEqual(Pair pair, Key key);
|
|
};
|
|
|
|
typedef DirectChainedHashMap<ObjectOffsetTrait> ObjectOffsetMap;
|
|
|
|
// A command which instructs the image writer to emit something into the ".text"
|
|
// segment.
|
|
//
|
|
// For now this supports
|
|
//
|
|
// * emitting the instructions of a [Code] object
|
|
// * emitting a trampoline of a certain size
|
|
//
|
|
struct ImageWriterCommand {
|
|
enum Opcode {
|
|
InsertInstructionOfCode,
|
|
InsertBytesOfTrampoline,
|
|
};
|
|
|
|
ImageWriterCommand(intptr_t expected_offset, CodePtr code)
|
|
: expected_offset(expected_offset),
|
|
op(ImageWriterCommand::InsertInstructionOfCode),
|
|
insert_instruction_of_code({code}) {}
|
|
|
|
ImageWriterCommand(intptr_t expected_offset,
|
|
uint8_t* trampoline_bytes,
|
|
intptr_t trampoline_length)
|
|
: expected_offset(expected_offset),
|
|
op(ImageWriterCommand::InsertBytesOfTrampoline),
|
|
insert_trampoline_bytes({trampoline_bytes, trampoline_length}) {}
|
|
|
|
// The offset (relative to the very first [ImageWriterCommand]) we expect
|
|
// this [ImageWriterCommand] to have.
|
|
intptr_t expected_offset;
|
|
|
|
Opcode op;
|
|
union {
|
|
struct {
|
|
CodePtr code;
|
|
} insert_instruction_of_code;
|
|
struct {
|
|
uint8_t* buffer;
|
|
intptr_t buffer_length;
|
|
} insert_trampoline_bytes;
|
|
};
|
|
};
|
|
|
|
class ImageWriter : public ValueObject {
|
|
public:
|
|
explicit ImageWriter(Thread* thread, bool generates_assembly);
|
|
virtual ~ImageWriter() {}
|
|
|
|
// Alignment constants used in writing ELF or assembly snapshots.
|
|
|
|
// BSS sections contain word-sized data.
|
|
static constexpr intptr_t kBssAlignment = compiler::target::kWordSize;
|
|
// ROData sections contain objects wrapped in an Image object.
|
|
static constexpr intptr_t kRODataAlignment = kObjectStartAlignment;
|
|
// Text sections contain objects (even in bare instructions mode) wrapped
|
|
// in an Image object.
|
|
static constexpr intptr_t kTextAlignment = kObjectStartAlignment;
|
|
|
|
void ResetOffsets() {
|
|
next_data_offset_ = Image::kHeaderSize;
|
|
next_text_offset_ = Image::kHeaderSize;
|
|
#if defined(DART_PRECOMPILER)
|
|
if (FLAG_precompiled_mode) {
|
|
// We reserve space for the initial InstructionsSection object. It is
|
|
// manually serialized since it includes offsets to other snapshot parts.
|
|
// It contains all the payloads which start directly after the header.
|
|
next_text_offset_ += compiler::target::InstructionsSection::HeaderSize();
|
|
}
|
|
#endif
|
|
objects_.Clear();
|
|
instructions_.Clear();
|
|
}
|
|
|
|
// Will start preparing the ".text" segment by interpreting the provided
|
|
// [ImageWriterCommand]s.
|
|
void PrepareForSerialization(GrowableArray<ImageWriterCommand>* commands);
|
|
|
|
bool IsROSpace() const {
|
|
return offset_space_ == IdSpace::kVmData ||
|
|
offset_space_ == IdSpace::kVmText ||
|
|
offset_space_ == IdSpace::kIsolateData ||
|
|
offset_space_ == IdSpace::kIsolateText;
|
|
}
|
|
int32_t GetTextOffsetFor(InstructionsPtr instructions, CodePtr code);
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
uint32_t GetDataOffsetFor(ObjectPtr raw_object, ObjectPtr raw_parent);
|
|
#else
|
|
uint32_t GetDataOffsetFor(ObjectPtr raw_object);
|
|
#endif
|
|
|
|
uint32_t AddBytesToData(uint8_t* bytes, intptr_t length);
|
|
|
|
void Write(NonStreamingWriteStream* clustered_stream, bool vm);
|
|
intptr_t data_size() const { return next_data_offset_; }
|
|
intptr_t text_size() const { return next_text_offset_; }
|
|
intptr_t GetTextObjectCount() const;
|
|
void GetTrampolineInfo(intptr_t* count, intptr_t* size) const;
|
|
|
|
void DumpStatistics();
|
|
|
|
void SetProfileWriter(V8SnapshotProfileWriter* profile_writer) {
|
|
profile_writer_ = profile_writer;
|
|
}
|
|
|
|
void ClearProfileWriter() { profile_writer_ = nullptr; }
|
|
|
|
void TraceInstructions(const Instructions& instructions);
|
|
|
|
static intptr_t SizeInSnapshot(ObjectPtr object);
|
|
static intptr_t SizeInSnapshot(const Object& object) {
|
|
return SizeInSnapshot(object.ptr());
|
|
}
|
|
|
|
// Returns nullptr if there is no profile writer.
|
|
const char* ObjectTypeForProfile(const Object& object) const;
|
|
static const char* TagObjectTypeAsReadOnly(Zone* zone, const char* type);
|
|
|
|
enum class ProgramSection {
|
|
Text, // Instructions.
|
|
Data, // Read-only data.
|
|
Bss, // Statically allocated variables initialized at load.
|
|
BuildId, // GNU build ID (when applicable)
|
|
// Adjust kNumProgramSections below to use last enum value added.
|
|
};
|
|
|
|
static constexpr intptr_t kNumProgramSections =
|
|
static_cast<int>(ProgramSection::BuildId) + 1;
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
// Returns a predetermined label for the given section in the VM isolate
|
|
// (if vm is true) or application isolate (otherwise) section. Some sections
|
|
// are shared by both.
|
|
static constexpr intptr_t SectionLabel(ProgramSection section, bool vm) {
|
|
// Both vm and isolate share the build id section.
|
|
const bool shared = section == ProgramSection::BuildId;
|
|
// The initial 1 is to ensure the result is positive.
|
|
return 1 + 2 * static_cast<int>(section) + ((shared || vm) ? 0 : 1);
|
|
}
|
|
#endif
|
|
|
|
protected:
|
|
virtual void WriteBss(bool vm) = 0;
|
|
virtual void WriteROData(NonStreamingWriteStream* clustered_stream, bool vm);
|
|
void WriteText(bool vm);
|
|
|
|
// Returns the standard Dart dynamic symbol name for the given VM isolate (if
|
|
// vm is true) or application isolate (otherwise) section. Some sections are
|
|
// shared by both.
|
|
static const char* SectionSymbol(ProgramSection section, bool vm);
|
|
|
|
static uword GetMarkedTags(classid_t cid,
|
|
intptr_t size,
|
|
bool is_canonical = false);
|
|
static uword GetMarkedTags(const Object& obj);
|
|
|
|
void DumpInstructionStats();
|
|
void DumpInstructionsSizes();
|
|
|
|
struct InstructionsData {
|
|
InstructionsData(InstructionsPtr insns, CodePtr code, intptr_t text_offset)
|
|
: raw_insns_(insns),
|
|
raw_code_(code),
|
|
text_offset_(text_offset),
|
|
trampoline_bytes(nullptr),
|
|
trampoline_length(0) {}
|
|
|
|
InstructionsData(uint8_t* trampoline_bytes,
|
|
intptr_t trampoline_length,
|
|
intptr_t text_offset)
|
|
: raw_insns_(nullptr),
|
|
raw_code_(nullptr),
|
|
text_offset_(text_offset),
|
|
trampoline_bytes(trampoline_bytes),
|
|
trampoline_length(trampoline_length) {}
|
|
|
|
union {
|
|
InstructionsPtr raw_insns_;
|
|
const Instructions* insns_;
|
|
};
|
|
union {
|
|
CodePtr raw_code_;
|
|
const Code* code_;
|
|
};
|
|
intptr_t text_offset_;
|
|
|
|
uint8_t* trampoline_bytes;
|
|
intptr_t trampoline_length;
|
|
};
|
|
|
|
struct ObjectData {
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
explicit ObjectData(ObjectPtr raw_obj, ObjectPtr raw_parent)
|
|
: raw_obj(raw_obj),
|
|
raw_parent(raw_parent),
|
|
flags(IsObjectField::encode(true) |
|
|
IsOriginalObjectField::encode(true)) {}
|
|
ObjectData(uint8_t* buf, intptr_t length)
|
|
: bytes({buf, length}),
|
|
raw_parent(Object::null()),
|
|
flags(IsObjectField::encode(false) |
|
|
IsOriginalObjectField::encode(false)) {}
|
|
#else
|
|
explicit ObjectData(ObjectPtr raw_obj)
|
|
: raw_obj(raw_obj),
|
|
flags(IsObjectField::encode(true) |
|
|
IsOriginalObjectField::encode(true)) {}
|
|
ObjectData(uint8_t* buf, intptr_t length)
|
|
: bytes({buf, length}),
|
|
flags(IsObjectField::encode(false) |
|
|
IsOriginalObjectField::encode(false)) {}
|
|
#endif
|
|
|
|
union {
|
|
struct {
|
|
uint8_t* buf;
|
|
intptr_t length;
|
|
} bytes;
|
|
ObjectPtr raw_obj;
|
|
const Object* obj;
|
|
};
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
union {
|
|
ObjectPtr raw_parent;
|
|
const Object* parent;
|
|
};
|
|
#endif
|
|
uint8_t flags;
|
|
|
|
bool is_object() const {
|
|
return IsObjectField::decode(flags);
|
|
}
|
|
bool is_original_object() const {
|
|
return IsOriginalObjectField::decode(flags);
|
|
}
|
|
|
|
void set_is_object(bool value) {
|
|
flags = IsObjectField::update(value, flags);
|
|
}
|
|
|
|
using IsObjectField = BitField<uint8_t, bool, 0, 1>;
|
|
using IsOriginalObjectField =
|
|
BitField<uint8_t, bool, IsObjectField::kNextBit, 1>;
|
|
};
|
|
|
|
// Methods abstracting out the particulars of the underlying concrete writer.
|
|
|
|
// Marks the entrance into a particular ProgramSection for either the VM
|
|
// isolate (if vm is true) or application isolate (if not). Returns false if
|
|
// this section should not be written.
|
|
virtual bool EnterSection(ProgramSection name,
|
|
bool vm,
|
|
intptr_t alignment,
|
|
intptr_t* alignment_padding = nullptr) = 0;
|
|
// Marks the exit from a particular ProgramSection, allowing subclasses to
|
|
// do any post-writing work.
|
|
virtual void ExitSection(ProgramSection name, bool vm, intptr_t size) = 0;
|
|
// Writes a prologue to the text section that describes how to interpret
|
|
// Dart stack frames using DWARF's Call Frame Information (CFI).
|
|
virtual void FrameUnwindPrologue() = 0;
|
|
// Writes an epilogue to the text section that marks the end of instructions
|
|
// covered by the CFI information in the prologue.
|
|
virtual void FrameUnwindEpilogue() = 0;
|
|
// Writes a target uword-sized value to the section contents.
|
|
virtual intptr_t WriteTargetWord(word value) = 0;
|
|
// Writes a sequence of bytes of length [size] from address [bytes] to the
|
|
// section contents.
|
|
virtual intptr_t WriteBytes(const void* bytes, intptr_t size) = 0;
|
|
// Pads the section contents to a given alignment with zeroes.
|
|
virtual intptr_t Align(intptr_t alignment,
|
|
intptr_t offset,
|
|
intptr_t position) = 0;
|
|
#if defined(DART_PRECOMPILER)
|
|
// Writes a target word-sized value that depends on the final relocated
|
|
// addresses of the sections named by the two symbols. If T is the final
|
|
// relocated address of the target section and S is the final relocated
|
|
// address of the source, the final value is:
|
|
// (T + target_offset + target_addend) - (S + source_offset)
|
|
virtual intptr_t Relocation(intptr_t section_offset,
|
|
intptr_t source_label,
|
|
intptr_t source_offset,
|
|
intptr_t target_label,
|
|
intptr_t target_offset) = 0;
|
|
// Writes a target word-sized value that contains the relocated address
|
|
// pointed to by the given symbol.
|
|
virtual intptr_t RelocatedAddress(intptr_t section_offset,
|
|
intptr_t label) = 0;
|
|
// Creates a static symbol for the given Code object when appropriate.
|
|
virtual void AddCodeSymbol(const Code& code,
|
|
const char* symbol,
|
|
intptr_t section_offset) = 0;
|
|
// Creates a static symbol for a read-only data object when appropriate.
|
|
virtual void AddDataSymbol(const char* symbol,
|
|
intptr_t section_offset,
|
|
size_t size) = 0;
|
|
|
|
// Overloaded convenience versions of the above virtual methods.
|
|
|
|
// An overload of Relocation where the target and source offsets and
|
|
// target addend are 0.
|
|
intptr_t Relocation(intptr_t section_offset,
|
|
intptr_t source_label,
|
|
intptr_t target_label) {
|
|
return Relocation(section_offset, source_label, 0, target_label, 0);
|
|
}
|
|
#endif
|
|
// Writes a fixed-sized value of type T to the section contents.
|
|
template <typename T>
|
|
intptr_t WriteFixed(T value) {
|
|
return WriteBytes(&value, sizeof(value));
|
|
}
|
|
// Like Align, but instead of padding with zeroes, the appropriate break
|
|
// instruction for the target architecture is used.
|
|
intptr_t AlignWithBreakInstructions(intptr_t alignment, intptr_t offset);
|
|
|
|
Thread* const thread_;
|
|
Zone* const zone_;
|
|
intptr_t next_data_offset_;
|
|
intptr_t next_text_offset_;
|
|
GrowableArray<ObjectData> objects_;
|
|
GrowableArray<InstructionsData> instructions_;
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
class SnapshotTextObjectNamer : ValueObject {
|
|
public:
|
|
explicit SnapshotTextObjectNamer(Zone* zone, bool for_assembly)
|
|
: zone_(ASSERT_NOTNULL(zone)),
|
|
lib_(Library::Handle(zone)),
|
|
cls_(Class::Handle(zone)),
|
|
parent_(Function::Handle(zone)),
|
|
owner_(Object::Handle(zone)),
|
|
string_(String::Handle(zone)),
|
|
insns_(Instructions::Handle(zone)),
|
|
store_(IsolateGroup::Current()->object_store()),
|
|
for_assembly_(for_assembly),
|
|
usage_count_(zone) {}
|
|
|
|
const char* StubNameForType(const AbstractType& type) const;
|
|
|
|
// Returns a unique name for text data to use in symbols. The name is
|
|
// not assembly-safe and must be appropriately quoted in assembly output.
|
|
// Assumes that code in the InstructionsData has been allocated a handle.
|
|
const char* SnapshotNameFor(const InstructionsData& data);
|
|
// Returns a unique name for read-only data to use in symbols. The name is
|
|
// not assembly-safe and must be appropriately quoted in assembly output.
|
|
// Assumes that the ObjectData has already been converted to object handles.
|
|
const char* SnapshotNameFor(const ObjectData& data);
|
|
|
|
private:
|
|
// Returns a unique name for the given code or read-only data object for use
|
|
// in symbols. The name is not assembly-safe and must be appropriately
|
|
// quoted in assembly output.
|
|
const char* SnapshotNameFor(const Object& object);
|
|
// Adds a non-unique name for the given object to the given buffer.
|
|
void AddNonUniqueNameFor(BaseTextBuffer* buffer, const Object& object);
|
|
// Modifies the symbol name in the buffer as needed for assembly use.
|
|
void ModifyForAssembly(BaseTextBuffer* buffer);
|
|
|
|
Zone* const zone_;
|
|
Library& lib_;
|
|
Class& cls_;
|
|
Function& parent_;
|
|
Object& owner_;
|
|
String& string_;
|
|
Instructions& insns_;
|
|
ObjectStore* const store_;
|
|
// Used to decide whether we need to add a uniqueness suffix.
|
|
bool for_assembly_;
|
|
CStringIntMap usage_count_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SnapshotTextObjectNamer);
|
|
};
|
|
|
|
SnapshotTextObjectNamer namer_;
|
|
|
|
// Reserve two positive labels for each of the ProgramSection values (one for
|
|
// vm, one for isolate).
|
|
intptr_t next_label_ = 1 + 2 * kNumProgramSections;
|
|
#endif
|
|
|
|
IdSpace offset_space_ = IdSpace::kSnapshot;
|
|
V8SnapshotProfileWriter* profile_writer_ = nullptr;
|
|
const char* const image_type_;
|
|
const char* const instructions_section_type_;
|
|
const char* const instructions_type_;
|
|
const char* const trampoline_type_;
|
|
|
|
template <class T>
|
|
friend class TraceImageObjectScope;
|
|
|
|
private:
|
|
static intptr_t SizeInSnapshotForBytes(intptr_t length);
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ImageWriter);
|
|
};
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::Bss,
|
|
/*vm=*/true) == Elf::kVmBssLabel,
|
|
"unexpected label for VM BSS section");
|
|
static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::Bss,
|
|
/*vm=*/false) == Elf::kIsolateBssLabel,
|
|
"unexpected label for isolate BSS section");
|
|
static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::BuildId,
|
|
/*vm=*/true) == Elf::kBuildIdLabel,
|
|
"unexpected label for build id section");
|
|
static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::BuildId,
|
|
/*vm=*/false) == Elf::kBuildIdLabel,
|
|
"unexpected label for build id section");
|
|
|
|
#define AutoTraceImage(object, section_offset, stream) \
|
|
TraceImageObjectScope<std::remove_pointer<decltype(stream)>::type> \
|
|
AutoTraceImageObjectScopeVar##__COUNTER__(this, section_offset, stream, \
|
|
object);
|
|
|
|
template <typename T>
|
|
class TraceImageObjectScope : ValueObject {
|
|
public:
|
|
TraceImageObjectScope(ImageWriter* writer,
|
|
intptr_t section_offset,
|
|
const T* stream,
|
|
const Object& object)
|
|
: writer_(ASSERT_NOTNULL(writer)),
|
|
stream_(ASSERT_NOTNULL(stream)),
|
|
section_offset_(section_offset),
|
|
start_offset_(stream_->Position() - section_offset),
|
|
object_type_(writer->ObjectTypeForProfile(object)),
|
|
object_name_(object.IsString() ? object.ToCString() : nullptr) {}
|
|
|
|
~TraceImageObjectScope() {
|
|
if (writer_->profile_writer_ == nullptr) return;
|
|
ASSERT(writer_->IsROSpace());
|
|
writer_->profile_writer_->SetObjectTypeAndName(
|
|
{writer_->offset_space_, start_offset_}, object_type_, object_name_);
|
|
writer_->profile_writer_->AttributeBytesTo(
|
|
{writer_->offset_space_, start_offset_},
|
|
stream_->Position() - section_offset_ - start_offset_);
|
|
}
|
|
|
|
private:
|
|
ImageWriter* const writer_;
|
|
const T* const stream_;
|
|
const intptr_t section_offset_;
|
|
const intptr_t start_offset_;
|
|
const char* const object_type_;
|
|
const char* const object_name_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(TraceImageObjectScope);
|
|
};
|
|
|
|
class AssemblyImageWriter : public ImageWriter {
|
|
public:
|
|
AssemblyImageWriter(Thread* thread,
|
|
BaseWriteStream* stream,
|
|
bool strip = false,
|
|
Elf* debug_elf = nullptr);
|
|
void Finalize();
|
|
|
|
private:
|
|
virtual void WriteBss(bool vm);
|
|
virtual void WriteROData(NonStreamingWriteStream* clustered_stream, bool vm);
|
|
|
|
virtual bool EnterSection(ProgramSection section,
|
|
bool vm,
|
|
intptr_t alignment,
|
|
intptr_t* alignment_padding = nullptr);
|
|
virtual void ExitSection(ProgramSection name, bool vm, intptr_t size);
|
|
virtual intptr_t WriteTargetWord(word value);
|
|
virtual intptr_t WriteBytes(const void* bytes, intptr_t size);
|
|
virtual intptr_t Align(intptr_t alignment,
|
|
intptr_t offset,
|
|
intptr_t position);
|
|
virtual intptr_t Relocation(intptr_t section_offset,
|
|
intptr_t source_label,
|
|
intptr_t source_offset,
|
|
intptr_t target_label,
|
|
intptr_t target_offset);
|
|
virtual intptr_t RelocatedAddress(intptr_t section_offset, intptr_t label) {
|
|
// Cannot calculate snapshot-relative addresses in assembly snapshots.
|
|
return WriteTargetWord(Image::kNoRelocatedAddress);
|
|
}
|
|
virtual void FrameUnwindPrologue();
|
|
virtual void FrameUnwindEpilogue();
|
|
virtual void AddCodeSymbol(const Code& code,
|
|
const char* symbol,
|
|
intptr_t offset);
|
|
virtual void AddDataSymbol(const char* symbol, intptr_t offset, size_t size);
|
|
|
|
BaseWriteStream* const assembly_stream_;
|
|
Dwarf* const assembly_dwarf_;
|
|
Elf* const debug_elf_;
|
|
|
|
// Used in Relocation to output "(.)" for relocations involving the current
|
|
// section position.
|
|
intptr_t current_section_label_ = 0;
|
|
|
|
// Used for creating local symbols for code and data objects in the
|
|
// debugging info, if separately written.
|
|
ZoneGrowableArray<Elf::SymbolData>* current_symbols_ = nullptr;
|
|
|
|
// Maps labels to the appropriate symbol names for relocations and DWARF
|
|
// output.
|
|
IntMap<const char*> label_to_symbol_name_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AssemblyImageWriter);
|
|
};
|
|
#endif
|
|
|
|
class BlobImageWriter : public ImageWriter {
|
|
public:
|
|
BlobImageWriter(Thread* thread,
|
|
NonStreamingWriteStream* vm_instructions,
|
|
NonStreamingWriteStream* isolate_instructions,
|
|
Elf* debug_elf = nullptr,
|
|
Elf* elf = nullptr);
|
|
|
|
private:
|
|
virtual void WriteBss(bool vm);
|
|
virtual void WriteROData(NonStreamingWriteStream* clustered_stream, bool vm);
|
|
|
|
virtual bool EnterSection(ProgramSection section,
|
|
bool vm,
|
|
intptr_t alignment,
|
|
intptr_t* alignment_padding = nullptr);
|
|
virtual void ExitSection(ProgramSection name, bool vm, intptr_t size);
|
|
virtual intptr_t WriteTargetWord(word value);
|
|
virtual intptr_t WriteBytes(const void* bytes, intptr_t size);
|
|
virtual intptr_t Align(intptr_t alignment,
|
|
intptr_t offset,
|
|
intptr_t position);
|
|
// TODO(rmacnak): Generate .debug_frame / .eh_frame / .arm.exidx to
|
|
// provide unwinding information.
|
|
virtual void FrameUnwindPrologue() {}
|
|
virtual void FrameUnwindEpilogue() {}
|
|
#if defined(DART_PRECOMPILER)
|
|
virtual intptr_t Relocation(intptr_t section_offset,
|
|
intptr_t source_label,
|
|
intptr_t source_offset,
|
|
intptr_t target_label,
|
|
intptr_t target_offset);
|
|
virtual intptr_t RelocatedAddress(intptr_t section_offset, intptr_t label) {
|
|
return ImageWriter::Relocation(section_offset,
|
|
Elf::Relocation::kSnapshotRelative, label);
|
|
}
|
|
virtual void AddCodeSymbol(const Code& code,
|
|
const char* symbol,
|
|
intptr_t offset);
|
|
virtual void AddDataSymbol(const char* symbol, intptr_t offset, size_t size);
|
|
|
|
// Set on section entrance to a new array containing the relocations for the
|
|
// current section.
|
|
ZoneGrowableArray<Elf::Relocation>* current_relocations_ = nullptr;
|
|
// Set on section entrance to a new array containing the local symbol data
|
|
// for the current section.
|
|
ZoneGrowableArray<Elf::SymbolData>* current_symbols_ = nullptr;
|
|
#endif
|
|
|
|
NonStreamingWriteStream* const vm_instructions_;
|
|
NonStreamingWriteStream* const isolate_instructions_;
|
|
Elf* const elf_;
|
|
Elf* const debug_elf_;
|
|
|
|
// Set on section entrance to the stream that should be used by the writing
|
|
// methods.
|
|
NonStreamingWriteStream* current_section_stream_ = nullptr;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(BlobImageWriter);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_IMAGE_SNAPSHOT_H_
|