// 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.

#include "vm/so_writer.h"

#if defined(DART_PRECOMPILER)

#include "vm/image_snapshot.h"

namespace dart {

void SharedObjectWriter::WriteStream::WriteRelocatableValue(
    intptr_t address,
    const Relocation& reloc,
    intptr_t reloc_index) {
  intptr_t source_address = reloc.source_offset;
  switch (reloc.source_label) {
    case Relocation::kSelfRelative:
      source_address += address;
      break;
    case Relocation::kSnapshotRelative:
      // No change to source_address.
      break;
    default:
      ASSERT(reloc.source_label > 0);
      source_address += FindValueForLabel(reloc.source_label);
  }
  ASSERT(reloc.size_in_bytes <= kWordSize);
  word to_write = reloc.target_offset - source_address;
  switch (reloc.target_label) {
    case Relocation::kSelfRelative:
      to_write += address;
      break;
    case Relocation::kSnapshotRelative:
      // No change to to_write.
      break;
    default: {
      ASSERT(reloc.target_label > 0);
      intptr_t value;
      if (HasValueForLabel(reloc.target_label, &value)) {
        to_write += value;
      } else {
        ASSERT_EQUAL(reloc.target_label, kBuildIdLabel);
        ASSERT_EQUAL(reloc.target_offset, 0);
        ASSERT_EQUAL(reloc.source_offset, 0);
        ASSERT_EQUAL(reloc.size_in_bytes, compiler::target::kWordSize);
        // TODO(dartbug.com/43516): Special case for snapshots with deferred
        // sections that handles the build ID relocation in an
        // InstructionsSection when there is no build ID.
        to_write = Image::kNoRelocatedAddress;
      }
    }
  }
  ASSERT(Utils::IsInt(reloc.size_in_bytes * kBitsPerByte, to_write));
  WriteBytes(reinterpret_cast<const uint8_t*>(&to_write), reloc.size_in_bytes);
}

void SharedObjectWriter::WriteStream::WriteBytesWithRelocations(
    const uint8_t* bytes,
    intptr_t size,
    intptr_t start_address,
    const RelocationArray& relocations) {
  // Resolve relocations as we write.
  intptr_t current_pos = 0;
  for (intptr_t i = 0; i < relocations.length(); i++) {
    const auto& reloc = relocations[i];
    // We assume here that the relocations are sorted in increasing order,
    // with unique section offsets.
    const intptr_t preceding = reloc.section_offset - current_pos;
    if (preceding > 0) {
      WriteBytes(bytes + current_pos, preceding);
      current_pos += preceding;
    }
    ASSERT_EQUAL(current_pos, reloc.section_offset);
    WriteRelocatableValue(start_address + current_pos, reloc, i);
    current_pos += reloc.size_in_bytes;
  }
  WriteBytes(bytes + current_pos, size - current_pos);
}

}  // namespace dart

#endif  // DART_PRECOMPILER
