// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COURGETTE_ASSEMBLY_PROGRAM_H_
#define COURGETTE_ASSEMBLY_PROGRAM_H_

#include <stdint.h>

#include <vector>

#include "courgette/courgette.h"
#include "courgette/image_utils.h"
#include "courgette/instruction_utils.h"
#include "courgette/label_manager.h"
#include "courgette/memory_allocator.h"  // For CheckBool.

namespace courgette {

class EncodedProgram;

// An AssemblyProgram stores Labels extracted from an executable file, and
// (optionally) Label annotations. It is initialized by a Disassembler, but
// stores separate state so that the Disassembler can be deleted. Typical usage:
//
// * The Disassembler calls PrecomputeLabels() and injects RVAs for abs32/rel32
//   references. These are used to initialize labels.
// * The Disassembler calls DefaultAssignIndexes() to assign addresses to
//   positions in the address tables.
// * [Optional step]
// * The Disassembler can use Labels in AssemblyProgram to convert the
//   executable file to an EncodedProgram, serialized to an output stream.
// * Later, the Disassembler can use the AssemblyProgram to can be deserialized
//   and assembled into the original executable file via an EncodedProgram.
//
// The optional step is to adjust Labels in the AssemblyProgram. One form of
// adjustment is to assign indexes in such a way as to make the EncodedProgram
// for an executable look more like the EncodedProgram for another exectuable.
// The adjustment process should call UnassignIndexes(), do its own assignment,
// and then call AssignRemainingIndexes() to ensure all indexes are assigned.
class AssemblyProgram {
 public:
  AssemblyProgram(ExecutableType kind, uint64_t image_base);

  AssemblyProgram(const AssemblyProgram&) = delete;
  AssemblyProgram& operator=(const AssemblyProgram&) = delete;

  ~AssemblyProgram();

  ExecutableType kind() const { return kind_; }
  const std::vector<Label*>& abs32_label_annotations() const {
    return abs32_label_annotations_;
  }
  const std::vector<Label*>& rel32_label_annotations() const {
    return rel32_label_annotations_;
  }

  // Traverses RVAs in |abs32_visitor| and |rel32_visitor| to precompute Labels.
  void PrecomputeLabels(RvaVisitor* abs32_visitor, RvaVisitor* rel32_visitor);

  void UnassignIndexes();
  void DefaultAssignIndexes();
  void AssignRemainingIndexes();

  // Looks up abs32 label. Returns null if none found.
  Label* FindAbs32Label(RVA rva);

  // Looks up rel32 label. Returns null if none found.
  Label* FindRel32Label(RVA rva);

  // Uses |gen| to initializes |*_label_annotations_|.
  CheckBool AnnotateLabels(const InstructionGenerator& gen);

  // Initializes |encoded| by injecting basic data and Label data.
  bool PrepareEncodedProgram(EncodedProgram* encoded) const;

 private:
  static const int kLabelLowerLimit;

  // Looks up a label or creates a new one.  Might return nullptr.
  Label* FindLabel(RVA rva, RVAToLabel* labels);

  const ExecutableType kind_;
  const uint64_t image_base_;  // Desired or mandated base address of image.

  // Storage and lookup of Labels associated with target addresses. We use
  // separate abs32 and rel32 labels.
  LabelManager abs32_label_manager_;
  LabelManager rel32_label_manager_;

  // Label pointers for each abs32 and rel32 location, sorted by file offset.
  // These are used by Label adjustment during patch generation.
  std::vector<Label*> abs32_label_annotations_;
  std::vector<Label*> rel32_label_annotations_;
};

}  // namespace courgette

#endif  // COURGETTE_ASSEMBLY_PROGRAM_H_