1// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_VM_IMAGE_SNAPSHOT_H_
6#define RUNTIME_VM_IMAGE_SNAPSHOT_H_
7
8#include <memory>
9#include <utility>
10
11#include "platform/assert.h"
12#include "platform/utils.h"
13#include "vm/allocation.h"
14#include "vm/compiler/runtime_api.h"
15#include "vm/datastream.h"
16#include "vm/globals.h"
17#include "vm/growable_array.h"
18#include "vm/hash_map.h"
19#include "vm/object.h"
20#include "vm/reusable_handles.h"
21#include "vm/type_testing_stubs.h"
22#include "vm/v8_snapshot_writer.h"
23
24namespace dart {
25
26// Forward declarations.
27class Code;
28class Dwarf;
29class Elf;
30class Instructions;
31class Object;
32
33class Image : ValueObject {
34 public:
35 explicit Image(const void* raw_memory) : raw_memory_(raw_memory) {
36 ASSERT(Utils::IsAligned(raw_memory, kMaxObjectAlignment));
37 }
38
39 void* object_start() const {
40 return reinterpret_cast<void*>(reinterpret_cast<uword>(raw_memory_) +
41 kHeaderSize);
42 }
43
44 uword object_size() const {
45 uword snapshot_size = *reinterpret_cast<const uword*>(raw_memory_);
46 return snapshot_size - kHeaderSize;
47 }
48
49 bool contains(uword address) const {
50 uword start = reinterpret_cast<uword>(object_start());
51 return address >= start && (address - start < object_size());
52 }
53
54 // Returns the offset of the BSS section from this image. Only has meaning for
55 // instructions images.
56 word bss_offset() const {
57 auto const raw_value = *(reinterpret_cast<const word*>(raw_memory_) + 1);
58 return Utils::RoundDown(raw_value, kBssAlignment);
59 }
60
61 // Returns true if the image was compiled directly to ELF. Only has meaning
62 // for instructions images.
63 bool compiled_to_elf() const {
64 auto const raw_value = *(reinterpret_cast<const word*>(raw_memory_) + 1);
65 return (raw_value & 0x1) == 0x1;
66 }
67
68 private:
69 static constexpr intptr_t kHeaderFields = 2;
70 static constexpr intptr_t kHeaderSize = kMaxObjectAlignment;
71 // Explicitly double-checking kHeaderSize is never changed. Increasing the
72 // Image header size would mean objects would not start at a place expected
73 // by parts of the VM (like the GC) that use Image pages as HeapPages.
74 static_assert(kHeaderSize == kMaxObjectAlignment,
75 "Image page cannot be used as HeapPage");
76
77 // Determines how many bits we have for encoding any extra information in
78 // the BSS offset.
79 static constexpr intptr_t kBssAlignment = compiler::target::kWordSize;
80
81 const void* raw_memory_; // The symbol kInstructionsSnapshot.
82
83 // For access to private constants.
84 friend class AssemblyImageWriter;
85 friend class BlobImageWriter;
86 friend class ImageWriter;
87 friend class Elf;
88
89 DISALLOW_COPY_AND_ASSIGN(Image);
90};
91
92class ImageReader : public ZoneAllocated {
93 public:
94 ImageReader(const uint8_t* data_image, const uint8_t* instructions_image);
95
96 ApiErrorPtr VerifyAlignment() const;
97
98 ONLY_IN_PRECOMPILED(uword GetBareInstructionsAt(uint32_t offset) const);
99 ONLY_IN_PRECOMPILED(uword GetBareInstructionsEnd() const);
100 InstructionsPtr GetInstructionsAt(uint32_t offset) const;
101 ObjectPtr GetObjectAt(uint32_t offset) const;
102
103 private:
104 const uint8_t* data_image_;
105 const uint8_t* instructions_image_;
106
107 DISALLOW_COPY_AND_ASSIGN(ImageReader);
108};
109
110struct ObjectOffsetPair {
111 public:
112 ObjectOffsetPair() : ObjectOffsetPair(NULL, 0) {}
113 ObjectOffsetPair(ObjectPtr obj, int32_t off) : object(obj), offset(off) {}
114
115 ObjectPtr object;
116 int32_t offset;
117};
118
119class ObjectOffsetTrait {
120 public:
121 // Typedefs needed for the DirectChainedHashMap template.
122 typedef ObjectPtr Key;
123 typedef int32_t Value;
124 typedef ObjectOffsetPair Pair;
125
126 static Key KeyOf(Pair kv) { return kv.object; }
127 static Value ValueOf(Pair kv) { return kv.offset; }
128 static intptr_t Hashcode(Key key);
129 static inline bool IsKeyEqual(Pair pair, Key key);
130};
131
132typedef DirectChainedHashMap<ObjectOffsetTrait> ObjectOffsetMap;
133
134// A command which instructs the image writer to emit something into the ".text"
135// segment.
136//
137// For now this supports
138//
139// * emitting the instructions of a [Code] object
140// * emitting a trampoline of a certain size
141//
142struct ImageWriterCommand {
143 enum Opcode {
144 InsertInstructionOfCode,
145 InsertBytesOfTrampoline,
146 };
147
148 ImageWriterCommand(intptr_t expected_offset, CodePtr code)
149 : expected_offset(expected_offset),
150 op(ImageWriterCommand::InsertInstructionOfCode),
151 insert_instruction_of_code({code}) {}
152
153 ImageWriterCommand(intptr_t expected_offset,
154 uint8_t* trampoline_bytes,
155 intptr_t trampoine_length)
156 : expected_offset(expected_offset),
157 op(ImageWriterCommand::InsertBytesOfTrampoline),
158 insert_trampoline_bytes({trampoline_bytes, trampoine_length}) {}
159
160 // The offset (relative to the very first [ImageWriterCommand]) we expect
161 // this [ImageWriterCommand] to have.
162 intptr_t expected_offset;
163
164 Opcode op;
165 union {
166 struct {
167 CodePtr code;
168 } insert_instruction_of_code;
169 struct {
170 uint8_t* buffer;
171 intptr_t buffer_length;
172 } insert_trampoline_bytes;
173 };
174};
175
176class ImageWriter : public ValueObject {
177 public:
178 explicit ImageWriter(Thread* thread);
179 virtual ~ImageWriter() {}
180
181 void ResetOffsets() {
182 next_data_offset_ = Image::kHeaderSize;
183 next_text_offset_ = Image::kHeaderSize;
184 if (FLAG_use_bare_instructions && FLAG_precompiled_mode) {
185 next_text_offset_ += compiler::target::InstructionsSection::HeaderSize();
186 }
187 objects_.Clear();
188 instructions_.Clear();
189 }
190
191 // Will start preparing the ".text" segment by interpreting the provided
192 // [ImageWriterCommand]s.
193 void PrepareForSerialization(GrowableArray<ImageWriterCommand>* commands);
194
195 bool IsROSpace() const {
196 return offset_space_ == V8SnapshotProfileWriter::kVmData ||
197 offset_space_ == V8SnapshotProfileWriter::kVmText ||
198 offset_space_ == V8SnapshotProfileWriter::kIsolateData ||
199 offset_space_ == V8SnapshotProfileWriter::kIsolateText;
200 }
201 int32_t GetTextOffsetFor(InstructionsPtr instructions, CodePtr code);
202 uint32_t GetDataOffsetFor(ObjectPtr raw_object);
203
204 void Write(WriteStream* clustered_stream, bool vm);
205 intptr_t data_size() const { return next_data_offset_; }
206 intptr_t text_size() const { return next_text_offset_; }
207 intptr_t GetTextObjectCount() const;
208 void GetTrampolineInfo(intptr_t* count, intptr_t* size) const;
209
210 void DumpStatistics();
211
212 void SetProfileWriter(V8SnapshotProfileWriter* profile_writer) {
213 profile_writer_ = profile_writer;
214 }
215
216 void ClearProfileWriter() { profile_writer_ = nullptr; }
217
218 void TraceInstructions(const Instructions& instructions);
219
220 static intptr_t SizeInSnapshot(ObjectPtr object);
221 static const intptr_t kBareInstructionsAlignment = 4;
222
223 static_assert(
224 (kObjectAlignmentLog2 -
225 compiler::target::ObjectAlignment::kObjectAlignmentLog2) >= 0,
226 "Target object alignment is larger than the host object alignment");
227
228 // Converts the target object size (in bytes) to an appropriate argument for
229 // ObjectLayout::SizeTag methods on the host machine.
230 //
231 // ObjectLayout::SizeTag expects a size divisible by kObjectAlignment and
232 // checks this in debug mode, but the size on the target machine may not be
233 // divisible by the host machine's object alignment if they differ.
234 //
235 // If target_size = n, we convert it to n * m, where m is the host alignment
236 // divided by the target alignment. This means AdjustObjectSizeForTarget(n)
237 // encodes on the host machine to the same bits that decode to n on the target
238 // machine. That is:
239 // n * (host align / target align) / host align => n / target align
240 static constexpr intptr_t AdjustObjectSizeForTarget(intptr_t target_size) {
241 return target_size
242 << (kObjectAlignmentLog2 -
243 compiler::target::ObjectAlignment::kObjectAlignmentLog2);
244 }
245
246 static UNLESS_DEBUG(constexpr) compiler::target::uword
247 UpdateObjectSizeForTarget(intptr_t size, uword marked_tags) {
248 return ObjectLayout::SizeTag::update(AdjustObjectSizeForTarget(size),
249 marked_tags);
250 }
251
252 // Returns nullptr if there is no profile writer.
253 const char* ObjectTypeForProfile(const Object& object) const;
254 static const char* TagObjectTypeAsReadOnly(Zone* zone, const char* type);
255
256 protected:
257 void WriteROData(WriteStream* stream);
258 virtual void WriteText(WriteStream* clustered_stream, bool vm) = 0;
259
260 void DumpInstructionStats();
261 void DumpInstructionsSizes();
262
263 struct InstructionsData {
264 InstructionsData(InstructionsPtr insns, CodePtr code, intptr_t text_offset)
265 : raw_insns_(insns),
266 raw_code_(code),
267 text_offset_(text_offset),
268 trampoline_bytes(nullptr),
269 trampoline_length(0) {}
270
271 InstructionsData(uint8_t* trampoline_bytes,
272 intptr_t trampoline_length,
273 intptr_t text_offset)
274 : raw_insns_(nullptr),
275 raw_code_(nullptr),
276 text_offset_(text_offset),
277 trampoline_bytes(trampoline_bytes),
278 trampoline_length(trampoline_length) {}
279
280 union {
281 InstructionsPtr raw_insns_;
282 const Instructions* insns_;
283 };
284 union {
285 CodePtr raw_code_;
286 const Code* code_;
287 };
288 intptr_t text_offset_;
289
290 uint8_t* trampoline_bytes;
291 intptr_t trampoline_length;
292 };
293
294 struct ObjectData {
295 explicit ObjectData(ObjectPtr raw_obj) : raw_obj_(raw_obj) {}
296
297 union {
298 ObjectPtr raw_obj_;
299 const Object* obj_;
300 };
301 };
302
303 Heap* heap_; // Used for mapping RawInstructiosn to object ids.
304 intptr_t next_data_offset_;
305 intptr_t next_text_offset_;
306 GrowableArray<ObjectData> objects_;
307 GrowableArray<InstructionsData> instructions_;
308
309 V8SnapshotProfileWriter::IdSpace offset_space_ =
310 V8SnapshotProfileWriter::kSnapshot;
311 V8SnapshotProfileWriter* profile_writer_ = nullptr;
312 const char* const instructions_section_type_;
313 const char* const instructions_type_;
314 const char* const trampoline_type_;
315
316 template <class T>
317 friend class TraceImageObjectScope;
318 friend class SnapshotTextObjectNamer; // For InstructionsData.
319
320 private:
321 DISALLOW_COPY_AND_ASSIGN(ImageWriter);
322};
323
324#define AutoTraceImage(object, section_offset, stream) \
325 auto AutoTraceImagObjectScopeVar##__COUNTER__ = \
326 TraceImageObjectScope<std::remove_pointer<decltype(stream)>::type>( \
327 this, section_offset, stream, object);
328
329template <typename T>
330class TraceImageObjectScope {
331 public:
332 TraceImageObjectScope(ImageWriter* writer,
333 intptr_t section_offset,
334 const T* stream,
335 const Object& object)
336 : writer_(ASSERT_NOTNULL(writer)),
337 stream_(ASSERT_NOTNULL(stream)),
338 section_offset_(section_offset),
339 start_offset_(stream_->Position() - section_offset),
340 object_(object) {}
341
342 ~TraceImageObjectScope() {
343 if (writer_->profile_writer_ == nullptr) return;
344 ASSERT(writer_->IsROSpace());
345 writer_->profile_writer_->SetObjectTypeAndName(
346 {writer_->offset_space_, start_offset_},
347 writer_->ObjectTypeForProfile(object_), nullptr);
348 writer_->profile_writer_->AttributeBytesTo(
349 {writer_->offset_space_, start_offset_},
350 stream_->Position() - section_offset_ - start_offset_);
351 }
352
353 private:
354 ImageWriter* const writer_;
355 const T* const stream_;
356 const intptr_t section_offset_;
357 const intptr_t start_offset_;
358 const Object& object_;
359};
360
361class SnapshotTextObjectNamer {
362 public:
363 explicit SnapshotTextObjectNamer(Zone* zone)
364 : zone_(zone),
365 owner_(Object::Handle(zone)),
366 string_(String::Handle(zone)),
367 insns_(Instructions::Handle(zone)),
368 store_(Isolate::Current()->object_store()) {}
369
370 const char* StubNameForType(const AbstractType& type) const;
371
372 const char* SnapshotNameFor(intptr_t code_index, const Code& code);
373 const char* SnapshotNameFor(intptr_t index,
374 const ImageWriter::InstructionsData& data);
375
376 private:
377 Zone* const zone_;
378 Object& owner_;
379 String& string_;
380 Instructions& insns_;
381 ObjectStore* const store_;
382 TypeTestingStubNamer namer_;
383};
384
385class AssemblyImageWriter : public ImageWriter {
386 public:
387 AssemblyImageWriter(Thread* thread,
388 Dart_StreamingWriteCallback callback,
389 void* callback_data,
390 bool strip = false,
391 Elf* debug_elf = nullptr);
392 void Finalize();
393
394 virtual void WriteText(WriteStream* clustered_stream, bool vm);
395
396 private:
397 void FrameUnwindPrologue();
398 void FrameUnwindEpilogue();
399 intptr_t WriteByteSequence(uword start, uword end);
400 intptr_t Align(intptr_t alignment, uword position = 0);
401
402#if defined(TARGET_ARCH_IS_64_BIT)
403 const char* kLiteralPrefix = ".quad";
404#else
405 const char* kLiteralPrefix = ".long";
406#endif
407
408 intptr_t WriteWordLiteralText(compiler::target::uword value) {
409 // Padding is helpful for comparing the .S with --disassemble.
410#if defined(TARGET_ARCH_IS_64_BIT)
411 assembly_stream_.Print(".quad 0x%0.16" Px "\n", value);
412#else
413 assembly_stream_.Print(".long 0x%0.8" Px "\n", value);
414#endif
415 return compiler::target::kWordSize;
416 }
417
418 StreamingWriteStream assembly_stream_;
419 Dwarf* assembly_dwarf_;
420 Elf* debug_elf_;
421
422 DISALLOW_COPY_AND_ASSIGN(AssemblyImageWriter);
423};
424
425class BlobImageWriter : public ImageWriter {
426 public:
427 BlobImageWriter(Thread* thread,
428 uint8_t** instructions_blob_buffer,
429 ReAlloc alloc,
430 intptr_t initial_size,
431 Elf* debug_elf = nullptr,
432 Elf* elf = nullptr);
433
434 virtual void WriteText(WriteStream* clustered_stream, bool vm);
435
436 intptr_t InstructionsBlobSize() const {
437 return instructions_blob_stream_.bytes_written();
438 }
439
440 private:
441 intptr_t WriteByteSequence(uword start, uword end);
442
443 WriteStream instructions_blob_stream_;
444 Elf* const elf_;
445 Elf* const debug_elf_;
446
447 DISALLOW_COPY_AND_ASSIGN(BlobImageWriter);
448};
449
450} // namespace dart
451
452#endif // RUNTIME_VM_IMAGE_SNAPSHOT_H_
453