| // Copyright (c) 2025, 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_SO_WRITER_H_ |
| #define RUNTIME_VM_SO_WRITER_H_ |
| |
| #include "platform/globals.h" |
| |
| #if defined(DART_PRECOMPILER) |
| #include "vm/allocation.h" |
| #include "vm/compiler/runtime_api.h" |
| #include "vm/datastream.h" |
| #include "vm/growable_array.h" |
| #include "vm/zone.h" |
| |
| namespace dart { |
| |
| class Dwarf; |
| class ElfWriter; |
| class MachOWriter; |
| |
| class SharedObjectWriter : public ZoneAllocated { |
| public: |
| enum class Type { |
| // A snapshot that should include segment contents. |
| Snapshot, |
| // Separately compiled debugging information that should not include |
| // most segment contents. |
| DebugInfo, |
| }; |
| |
| enum class Output { |
| Elf, |
| MachO, |
| }; |
| |
| SharedObjectWriter(Zone* zone, |
| BaseWriteStream* stream, |
| Type type, |
| Dwarf* dwarf = nullptr) |
| : zone_(zone), unwrapped_stream_(stream), type_(type), dwarf_(dwarf) { |
| // Separate debugging information should always have a Dwarf object. |
| ASSERT(type == Type::Snapshot || dwarf != nullptr); |
| // Assumed by various offset logic in the subclasses. |
| ASSERT_EQUAL(stream->Position(), 0); |
| } |
| virtual ~SharedObjectWriter() {} |
| |
| virtual intptr_t page_size() const = 0; |
| virtual Output output() const = 0; |
| |
| static bool IsStripped(Dwarf* dwarf) { return dwarf == nullptr; } |
| bool IsStripped() const { return IsStripped(dwarf_); } |
| |
| Zone* zone() const { return zone_; } |
| Dwarf* dwarf() { return dwarf_; } |
| |
| // Stores the information needed to appropriately generate a |
| // relocation from the target to the source at the given section offset. |
| struct Relocation { |
| size_t size_in_bytes; |
| intptr_t section_offset; |
| intptr_t source_label; |
| intptr_t source_offset; |
| intptr_t target_label; |
| intptr_t target_offset; |
| |
| // Used when the corresponding offset is relative from the location of the |
| // relocation itself. |
| static constexpr intptr_t kSelfRelative = -1; |
| // Used when the corresponding offset is relative to the start of the |
| // snapshot. |
| static constexpr intptr_t kSnapshotRelative = -2; |
| |
| Relocation(size_t size_in_bytes, |
| intptr_t section_offset, |
| intptr_t source_label, |
| intptr_t source_offset, |
| intptr_t target_label, |
| intptr_t target_offset) |
| : size_in_bytes(size_in_bytes), |
| section_offset(section_offset), |
| source_label(source_label), |
| source_offset(source_offset), |
| target_label(target_label), |
| target_offset(target_offset) { |
| // Other than special values, all labels should be positive. |
| ASSERT(source_label > 0 || source_label == kSelfRelative || |
| source_label == kSnapshotRelative); |
| ASSERT(target_label > 0 || target_label == kSelfRelative || |
| target_label == kSnapshotRelative); |
| } |
| }; |
| |
| using RelocationArray = ZoneGrowableArray<Relocation>; |
| |
| // Stores the information needed to appropriately generate a symbol |
| // during finalization. |
| struct SymbolData { |
| enum class Type { |
| Section, |
| Function, |
| Object, |
| }; |
| |
| const char* name; |
| Type type; |
| intptr_t offset; |
| size_t size; |
| // A positive unique ID only used internally in the Dart VM, not part of |
| // the shared object output. |
| intptr_t label; |
| |
| SymbolData(const char* name, |
| Type type, |
| intptr_t offset, |
| size_t size, |
| intptr_t label) |
| : name(name), type(type), offset(offset), size(size), label(label) { |
| ASSERT(label > 0); |
| } |
| }; |
| |
| using SymbolDataArray = ZoneGrowableArray<SymbolData>; |
| |
| struct WriteStream : public AbstractWriteStream { |
| WriteStream() {} |
| |
| void WriteBytesWithRelocations(const uint8_t* bytes, |
| intptr_t size, |
| intptr_t start_address, |
| const RelocationArray& relocations); |
| |
| virtual bool HasValueForLabel(intptr_t label, intptr_t* value) const = 0; |
| intptr_t FindValueForLabel(intptr_t label) const { |
| intptr_t value = -1; |
| const bool valid = HasValueForLabel(label, &value); |
| if (!valid) { |
| FATAL("Expected symbol for label: %" Pd "", label); |
| } |
| return value; |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(WriteStream); |
| }; |
| |
| class DelegatingWriteStream : public WriteStream { |
| public: |
| DelegatingWriteStream(BaseWriteStream* stream, |
| const SharedObjectWriter& writer) |
| : WriteStream(), |
| stream_(ASSERT_NOTNULL(stream)), |
| start_(stream->Position()), |
| page_size_(writer.page_size()) { |
| // So that we can use the underlying stream's Align, as all alignments |
| // will be less than or equal to this alignment. |
| ASSERT(Utils::IsAligned(start_, page_size_)); |
| } |
| |
| // We return positions in terms of the local content that has been written, |
| // ignoring any previous content on the stream. |
| intptr_t Position() const override { return stream_->Position() - start_; } |
| void WriteBytes(const void* b, intptr_t size) override { |
| stream_->WriteBytes(b, size); |
| } |
| void WriteByte(uint8_t value) override { stream_->WriteByte(value); } |
| intptr_t Align(intptr_t alignment, intptr_t offset = 0) override { |
| ASSERT(Utils::IsPowerOfTwo(alignment)); |
| ASSERT(alignment <= page_size_); |
| return stream_->Align(alignment, offset); |
| } |
| |
| protected: |
| BaseWriteStream* const stream_; |
| |
| private: |
| const intptr_t start_; |
| const intptr_t page_size_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DelegatingWriteStream); |
| }; |
| |
| // Must be the same value as the values returned by ImageWriter::SectionLabel |
| // for the appropriate section and vm values. |
| static constexpr intptr_t kVmBssLabel = 5; |
| static constexpr intptr_t kIsolateBssLabel = 6; |
| static constexpr intptr_t kBuildIdLabel = 7; |
| |
| virtual void AddText(const char* name, |
| intptr_t label, |
| const uint8_t* bytes, |
| intptr_t size, |
| const RelocationArray* relocations, |
| const SymbolDataArray* symbols) = 0; |
| virtual void AddROData(const char* name, |
| intptr_t label, |
| const uint8_t* bytes, |
| intptr_t size, |
| const RelocationArray* relocations, |
| const SymbolDataArray* symbols) = 0; |
| |
| virtual void Finalize() = 0; |
| |
| virtual void AssertConsistency(const SharedObjectWriter* debug) const = 0; |
| |
| virtual const ElfWriter* AsElfWriter() const { return nullptr; } |
| virtual const MachOWriter* AsMachOWriter() const { return nullptr; } |
| |
| protected: |
| Zone* const zone_; |
| BaseWriteStream* const unwrapped_stream_; |
| const Type type_; |
| |
| // If nullptr, then the shared object file should be stripped of static |
| // information like the static symbol table. |
| Dwarf* const dwarf_; |
| }; |
| |
| } // namespace dart |
| |
| #endif // DART_PRECOMPILER |
| |
| #endif // RUNTIME_VM_SO_WRITER_H_ |