|  | // 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/globals.h" | 
|  | #include "vm/growable_array.h" | 
|  | #include "vm/hash_map.h" | 
|  | #include "vm/object.h" | 
|  | #include "vm/reusable_handles.h" | 
|  | #include "vm/so_writer.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 Instructions; | 
|  | class Object; | 
|  | class SharedObjectWriter; | 
|  |  | 
|  | 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 directly compiled to a shared | 
|  | // object. Only valid for instructions images from precompiled snapshots. | 
|  | bool compiled_to_shared_object() const; | 
|  |  | 
|  | // For snapshots directly compiled to a shared object, returns a pointer to | 
|  | // the beginning of the shared object in memory. Otherwise returns nullptr. | 
|  | const uint8_t* shared_object_start() const; | 
|  |  | 
|  | // Returns whether this instructions section was directly compiled to ELF. | 
|  | // Only valid for instructions images from precompiled snapshots. | 
|  | bool compiled_to_elf() const; | 
|  |  | 
|  | // Returns whether this instructions section was directly compiled to MachO. | 
|  | // Only valid for instructions images from precompiled snapshots. | 
|  | bool compiled_to_macho() const; | 
|  |  | 
|  | private: | 
|  | // For snapshots directly compiled to a shared object, returns a pointer to | 
|  | // the beginning of the build id container. Otherwise returns nullptr; | 
|  | const void* build_id_start() const; | 
|  |  | 
|  | // 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 BlobImageWriter; | 
|  | friend class ImageWriter; | 
|  | friend class SharedObjectWriter; | 
|  | friend class MachOHeader;  // For kHeaderSize. | 
|  |  | 
|  | 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 shared object 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 | 
|  |  | 
|  | virtual void Finalize() = 0; | 
|  |  | 
|  | 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>; | 
|  | using IsOriginalObjectField = | 
|  | BitField<uint8_t, bool, IsObjectField::kNextBit>; | 
|  | }; | 
|  |  | 
|  | // 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) == | 
|  | SharedObjectWriter::kVmBssLabel, | 
|  | "unexpected label for VM BSS section"); | 
|  | static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::Bss, | 
|  | /*vm=*/false) == | 
|  | SharedObjectWriter::kIsolateBssLabel, | 
|  | "unexpected label for isolate BSS section"); | 
|  | static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::BuildId, | 
|  | /*vm=*/true) == | 
|  | SharedObjectWriter::kBuildIdLabel, | 
|  | "unexpected label for build id section"); | 
|  | static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::BuildId, | 
|  | /*vm=*/false) == | 
|  | SharedObjectWriter::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, | 
|  | SharedObjectWriter* debug_so = nullptr); | 
|  | virtual 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_; | 
|  | SharedObjectWriter* const debug_so_; | 
|  |  | 
|  | // 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. | 
|  | SharedObjectWriter::SymbolDataArray* 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, | 
|  | SharedObjectWriter* debug_so = nullptr, | 
|  | SharedObjectWriter* so = nullptr); | 
|  | #else | 
|  | BlobImageWriter(Thread* thread, | 
|  | NonStreamingWriteStream* vm_instructions, | 
|  | NonStreamingWriteStream* isolate_instructions, | 
|  | SharedObjectWriter* debug_so = nullptr, | 
|  | SharedObjectWriter* so = nullptr); | 
|  | #endif | 
|  |  | 
|  | virtual 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); | 
|  | // 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, SharedObjectWriter::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. | 
|  | SharedObjectWriter::RelocationArray* current_relocations_ = nullptr; | 
|  | // Set on section entrance to a new array containing the local symbol data | 
|  | // for the current section. | 
|  | SharedObjectWriter::SymbolDataArray* current_symbols_ = nullptr; | 
|  | #endif | 
|  |  | 
|  | NonStreamingWriteStream* const vm_instructions_; | 
|  | NonStreamingWriteStream* const isolate_instructions_; | 
|  | SharedObjectWriter* const so_; | 
|  | SharedObjectWriter* const debug_so_; | 
|  |  | 
|  | // 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_ |