| // Copyright (c) 2026, 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_DEBUG_INFO_STREAM_H_ |
| #define RUNTIME_VM_DEBUG_INFO_STREAM_H_ |
| |
| #include "platform/globals.h" |
| |
| #if defined(DART_PRECOMPILER) |
| |
| #include "vm/datastream.h" |
| #include "vm/debug_info.h" |
| #include "vm/so_writer.h" |
| |
| namespace dart { |
| |
| class DebugInfoStream : public DebugInfoWriteStream { |
| public: |
| DebugInfoStream(Zone* zone, NonStreamingWriteStream* stream) |
| : zone_(ASSERT_NOTNULL(zone)), |
| stream_(ASSERT_NOTNULL(stream)), |
| relocations_(new (zone) SharedObjectWriter::RelocationArray()) {} |
| |
| static constexpr intptr_t kInitialBufferSize = 64 * KB; |
| |
| uint8_t* buffer() { return stream_->buffer(); } |
| const uint8_t* buffer() const { return stream_->buffer(); } |
| intptr_t bytes_written() const { return stream_->bytes_written(); } |
| intptr_t Position() const { return stream_->Position(); } |
| void SetPosition(intptr_t value) { stream_->SetPosition(value); } |
| |
| void sleb128(intptr_t value) override { stream_->WriteSLEB128(value); } |
| void uleb128(uintptr_t value) override { stream_->WriteLEB128(value); } |
| void u1(uint8_t value) override { stream_->WriteByte(value); } |
| void u2(uint16_t value) override { stream_->WriteFixed(value); } |
| void u4(uint32_t value) override { stream_->WriteFixed(value); } |
| void u8(uint64_t value) override { stream_->WriteFixed(value); } |
| void bytes(const void* addr, intptr_t len) { stream_->WriteBytes(addr, len); } |
| void string(const char* cstr) override { // NOLINT |
| // Unlike stream_->WriteString(), we want the null terminator written. |
| stream_->WriteBytes(cstr, strlen(cstr) + 1); |
| } |
| // The prefix is ignored for binary debug-info streams. |
| void WritePrefixedLength(const char* unused, |
| std::function<void()> body) override { |
| const intptr_t fixup = stream_->Position(); |
| // DWARF v2 sizes and CodeView subsection sizes are both 32-bit here. |
| u4(0); |
| const intptr_t start = stream_->Position(); |
| body(); |
| const intptr_t end = stream_->Position(); |
| stream_->SetPosition(fixup); |
| u4(end - start); |
| stream_->SetPosition(end); |
| } |
| // Shorthand for when working directly with DebugInfoStreams. |
| void WritePrefixedLength(std::function<void()> body) { |
| WritePrefixedLength(nullptr, body); |
| } |
| void WritePrefixedLengthU16(std::function<void()> body) { |
| const intptr_t fixup = stream_->Position(); |
| u2(0); |
| const intptr_t start = stream_->Position(); |
| body(); |
| const intptr_t end = stream_->Position(); |
| const intptr_t length = end - start; |
| ASSERT(length <= kMaxUint16); |
| stream_->SetPosition(fixup); |
| u2(static_cast<uint16_t>(length)); |
| stream_->SetPosition(end); |
| } |
| |
| uint8_t LoadU1(intptr_t offset) const { |
| ASSERT(offset >= 0); |
| ASSERT(offset < bytes_written()); |
| return stream_->buffer()[offset]; |
| } |
| |
| void StoreU1(intptr_t offset, uint8_t value) { |
| ASSERT(offset >= 0); |
| ASSERT(offset < bytes_written()); |
| stream_->buffer()[offset] = value; |
| } |
| |
| void OffsetFromSymbol(intptr_t label, |
| intptr_t offset, |
| size_t size = kAddressSize) override { |
| ASSERT(size > 0); |
| ASSERT(size <= static_cast<size_t>(kInt64Size)); |
| relocations_->Add({size, stream_->Position(), |
| SharedObjectWriter::Relocation::kSnapshotRelative, 0, |
| label, offset}); |
| const uint64_t placeholder = 0; // Resolved later. |
| stream_->WriteBytes(&placeholder, size); |
| } |
| void InitializeAbstractOrigins(intptr_t size) override { |
| abstract_origins_size_ = size; |
| abstract_origins_ = zone_->Alloc<uint32_t>(abstract_origins_size_); |
| } |
| void RegisterAbstractOrigin(intptr_t index) override { |
| ASSERT(abstract_origins_ != nullptr); |
| ASSERT(index < abstract_origins_size_); |
| abstract_origins_[index] = stream_->Position(); |
| } |
| void AbstractOrigin(intptr_t index) override { u4(abstract_origins_[index]); } |
| |
| // Generates the offset of the virtual address corresponding to the given |
| // symbol label from the current position in the output. That is, if |
| // X = the virtual address of the current position |
| // Y = the virtual address of the symbol |
| // then the value at the current position in the output is Y - X. |
| // |
| // If no size is provided, the size of the offset in the stream is |
| // the native word size. |
| void RelativeSymbolOffset(intptr_t label, size_t size = kAddressSize) { |
| relocations_->Add({size, stream_->Position(), |
| SharedObjectWriter::Relocation::kSelfRelative, 0, label, |
| 0}); |
| const uint64_t placeholder = 0; // Resolved later. |
| stream_->WriteBytes(&placeholder, size); |
| } |
| |
| intptr_t Align(intptr_t alignment, |
| intptr_t offset = 0, |
| uint8_t fill_byte = 0) { |
| return stream_->Align(alignment, offset, fill_byte); |
| } |
| |
| const SharedObjectWriter::RelocationArray* relocations() const { |
| return relocations_; |
| } |
| |
| protected: |
| Zone* const zone_; |
| NonStreamingWriteStream* const stream_; |
| SharedObjectWriter::RelocationArray* const relocations_ = nullptr; |
| uint32_t* abstract_origins_ = nullptr; |
| intptr_t abstract_origins_size_ = -1; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(DebugInfoStream); |
| }; |
| |
| } // namespace dart |
| |
| #endif // DART_PRECOMPILER |
| |
| #endif // RUNTIME_VM_DEBUG_INFO_STREAM_H_ |