| // 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(nullptr, 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 |
| // * emitting a padding of a certain size |
| // |
| struct ImageWriterCommand { |
| enum Opcode { |
| InsertInstructionOfCode, |
| InsertBytesOfTrampoline, |
| InsertPadding, |
| }; |
| |
| 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}) {} |
| |
| ImageWriterCommand(intptr_t expected_offset, intptr_t padding_length) |
| : expected_offset(expected_offset), |
| op(ImageWriterCommand::InsertPadding), |
| insert_padding({padding_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; |
| |
| struct { |
| intptr_t padding_length; |
| } insert_padding; |
| }; |
| }; |
| |
| #if defined(DART_PRECOMPILER) |
| template <typename T> |
| class Trie : public ZoneAllocated { |
| public: |
| // Returns whether [key] is a valid trie key (that is, a C string that |
| // contains only characters for which charIndex returns a non-negative value). |
| static bool IsValidKey(const char* key) { |
| for (intptr_t i = 0; key[i] != '\0'; i++) { |
| if (ChildIndex(key[i]) < 0) return false; |
| } |
| return true; |
| } |
| |
| // Adds a binding of [key] to [value] in [trie]. Assumes that the string in |
| // [key] is a valid trie key and does not already have a value in [trie]. |
| // |
| // If [trie] is nullptr, then a new trie is created and a pointer to the new |
| // trie is returned. Otherwise, [trie] will be returned. |
| static Trie<T>* AddString(Zone* zone, |
| Trie<T>* trie, |
| const char* key, |
| const T* value); |
| |
| // Adds a binding of [key] to [value]. Assumes that the string in [key] is a |
| // valid trie key and does not already have a value in this trie. |
| void AddString(Zone* zone, const char* key, const T* value) { |
| AddString(zone, this, key, value); |
| } |
| |
| // Looks up the value stored for [key] in [trie]. If one is not found, then |
| // nullptr is returned. |
| // |
| // If [end] is not nullptr, then the longest prefix of [key] that is a valid |
| // trie key prefix will be used for the lookup and the value pointed to by |
| // [end] is set to the index after that prefix. Otherwise, the whole [key] |
| // is used. |
| static const T* Lookup(const Trie<T>* trie, |
| const char* key, |
| intptr_t* end = nullptr); |
| |
| // Looks up the value stored for [key]. If one is not found, then nullptr is |
| // returned. |
| // |
| // If [end] is not nullptr, then the longest prefix of [key] that is a valid |
| // trie key prefix will be used for the lookup and the value pointed to by |
| // [end] is set to the index after that prefix. Otherwise, the whole [key] |
| // is used. |
| const T* Lookup(const char* key, intptr_t* end = nullptr) const { |
| return Lookup(this, key, end); |
| } |
| |
| private: |
| // Currently, only the following characters can appear in obfuscated names: |
| // '_', '@', '0-9', 'a-z', 'A-Z' |
| static constexpr intptr_t kNumValidChars = 64; |
| |
| Trie() { |
| for (intptr_t i = 0; i < kNumValidChars; i++) { |
| children_[i] = nullptr; |
| } |
| } |
| |
| static intptr_t ChildIndex(char c) { |
| if (c == '_') return 0; |
| if (c == '@') return 1; |
| if (c >= '0' && c <= '9') return ('9' - c) + 2; |
| if (c >= 'a' && c <= 'z') return ('z' - c) + 12; |
| if (c >= 'A' && c <= 'Z') return ('Z' - c) + 38; |
| return -1; |
| } |
| |
| const T* value_ = nullptr; |
| Trie<T>* children_[kNumValidChars]; |
| }; |
| |
| template <typename T> |
| Trie<T>* Trie<T>::AddString(Zone* zone, |
| Trie<T>* trie, |
| const char* key, |
| const T* value) { |
| ASSERT(key != nullptr); |
| if (trie == nullptr) { |
| trie = new (zone) Trie<T>(); |
| } |
| if (*key == '\0') { |
| ASSERT(trie->value_ == nullptr); |
| trie->value_ = value; |
| } else { |
| auto const index = ChildIndex(*key); |
| ASSERT(index >= 0 && index < kNumValidChars); |
| trie->children_[index] = |
| AddString(zone, trie->children_[index], key + 1, value); |
| } |
| |
| return trie; |
| } |
| |
| template <typename T> |
| const T* Trie<T>::Lookup(const Trie<T>* trie, const char* key, intptr_t* end) { |
| intptr_t i = 0; |
| for (; key[i] != '\0'; i++) { |
| auto const index = ChildIndex(key[i]); |
| ASSERT(index < kNumValidChars); |
| if (index < 0) { |
| if (end == nullptr) return nullptr; |
| break; |
| } |
| // Still find the longest valid trie prefix when no stored value. |
| if (trie == nullptr) continue; |
| trie = trie->children_[index]; |
| } |
| if (end != nullptr) { |
| *end = i; |
| } |
| if (trie == nullptr) return nullptr; |
| return trie->value_; |
| } |
| #endif |
| |
| class ImageWriter : public ValueObject { |
| public: |
| #if defined(DART_PRECOMPILER) |
| ImageWriter(Thread* thread, |
| bool generates_assembly, |
| const Trie<const char>* deobfuscation_trie = nullptr); |
| #else |
| ImageWriter(Thread* thread, bool generates_assembly); |
| #endif |
| 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); |
| } |
| |
| static Trie<const char>* CreateReverseObfuscationTrie(Thread* thread); |
| static const char* Deobfuscate(Zone* zone, |
| const Trie<const char>* trie, |
| const char* str); |
| #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, |
| const Trie<const char>* deobfuscation_trie, |
| bool for_assembly) |
| : zone_(ASSERT_NOTNULL(zone)), |
| deobfuscation_trie_(deobfuscation_trie), |
| 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_; |
| const Trie<const char>* const deobfuscation_trie_; |
| 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_; |
| const char* const padding_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, |
| const Trie<const char>* deobfuscation_trie = nullptr, |
| 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: |
| #if defined(DART_PRECOMPILER) |
| BlobImageWriter(Thread* thread, |
| NonStreamingWriteStream* vm_instructions, |
| NonStreamingWriteStream* isolate_instructions, |
| const Trie<const char>* deobfuscation_trie = nullptr, |
| Elf* debug_elf = nullptr, |
| Elf* elf = nullptr); |
| #else |
| BlobImageWriter(Thread* thread, |
| NonStreamingWriteStream* vm_instructions, |
| NonStreamingWriteStream* isolate_instructions, |
| Elf* debug_elf = nullptr, |
| Elf* elf = nullptr); |
| #endif |
| |
| 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_ |