// Copyright (c) 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

//  dynamic_images.h
//
//    Implements most of the function of the dyld API, but allowing an
//    arbitrary task to be introspected, unlike the dyld API which
//    only allows operation on the current task.  The current implementation
//    is limited to use by 32-bit tasks.

#ifndef CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__
#define CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__

#include <mach/mach.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <sys/types.h>

#include <string>
#include <vector>

#include "mach_vm_compat.h"

namespace google_breakpad {

using std::string;
using std::vector;

//==============================================================================
// The memory layout of this struct matches the dyld_image_info struct
// defined in "dyld_gdb.h" in the darwin source.
typedef struct dyld_image_info32 {
  uint32_t                   load_address_;  // struct mach_header*
  uint32_t                   file_path_;     // char*
  uint32_t                   file_mod_date_;
} dyld_image_info32;

typedef struct dyld_image_info64 {
  uint64_t                   load_address_;  // struct mach_header*
  uint64_t                   file_path_;     // char*
  uint64_t                   file_mod_date_;
} dyld_image_info64;

//==============================================================================
// This is as defined in "dyld_gdb.h" in the darwin source.
// _dyld_all_image_infos (in dyld) is a structure of this type
// which will be used to determine which dynamic code has been loaded.
typedef struct dyld_all_image_infos32 {
  uint32_t                      version;  // == 1 in Mac OS X 10.4
  uint32_t                      infoArrayCount;
  uint32_t                      infoArray;  // const struct dyld_image_info*
  uint32_t                      notification;
  bool                          processDetachedFromSharedRegion;
  // Only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later
  const struct mach_header*     dyldImageLoadAddress;
  uint32_t                      padding[14];
  // Only in version 12 (Mac OS X 10.7, iOS 4.3) and later
  uint32_t                      sharedCacheSlide;
  uint32_t                      padding1[6];
  // Only in version 15 (macOS 10.12, iOS 10.0) and later
  const char*                   dyldPath;
} dyld_all_image_infos32;

typedef struct dyld_all_image_infos64 {
  uint32_t                      version;  // == 1 in Mac OS X 10.4
  uint32_t                      infoArrayCount;
  uint64_t                      infoArray;  // const struct dyld_image_info*
  uint64_t                      notification;
  bool                          processDetachedFromSharedRegion;
  // Only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later
  const struct mach_header_64*  dyldImageLoadAddress;
  uint64_t                      padding[14];
  // Only in version 12 (Mac OS X 10.7, iOS 4.3) and later
  uint64_t                      sharedCacheSlide;
  uint64_t                      padding1[4];
  // Only in version 15 (macOS 10.12, iOS 10.0) and later
  const char*                   dyldPath;
} dyld_all_image_infos64;

// some typedefs to isolate 64/32 bit differences
#ifdef __LP64__
typedef mach_header_64 breakpad_mach_header;
typedef segment_command_64 breakpad_mach_segment_command;
#else
typedef mach_header breakpad_mach_header;
typedef segment_command breakpad_mach_segment_command;
#endif

// Bit in mach_header.flags that indicates whether or not the image is in the
// dyld shared cache. The dyld shared cache is a single image into which
// commonly used system dylibs and frameworks are incorporated. dyld maps it
// into every process at load time. The component images all have the same
// slide.
#define MH_SHAREDCACHE 0x80000000

// Helper functions to deal with 32-bit/64-bit Mach-O differences.
class DynamicImage;
template<typename MachBits>
bool FindTextSection(DynamicImage& image);

template<typename MachBits>
uint32_t GetFileTypeFromHeader(DynamicImage& image);

//==============================================================================
// Represents a single dynamically loaded mach-o image
class DynamicImage {
 public:
  DynamicImage(uint8_t *header,     // data is copied
               size_t header_size,  // includes load commands
               uint64_t load_address,
               string file_path,
               uintptr_t image_mod_date,
               mach_port_t task,
               cpu_type_t cpu_type,
               cpu_subtype_t cpu_subtype,
               ptrdiff_t shared_cache_slide,
               bool is_dyld)
    : header_(header, header + header_size),
      header_size_(header_size),
      load_address_(load_address),
      vmaddr_(0),
      vmsize_(0),
      slide_(0),
      crash_info_(),
      version_(0),
      file_path_(file_path),
      file_mod_date_(image_mod_date),
      is_dyld_(is_dyld),
      task_(task),
      cpu_type_(cpu_type),
      cpu_subtype_(cpu_subtype),
      shared_cache_slide_(shared_cache_slide) {
    CalculateMemoryAndVersionInfo();
  }

  // Size of mach_header plus load commands
  size_t GetHeaderSize() const {return header_.size();}

  // Full path to mach-o binary
  string GetFilePath() {return file_path_;}

  uint64_t GetModDate() const {return file_mod_date_;}

  // Actual address where the image was loaded
  uint64_t GetLoadAddress() const {return load_address_;}

  // Address where the image should be loaded
  mach_vm_address_t GetVMAddr() const {return vmaddr_;}

  bool GetInDyldSharedCache()
    {return (shared_cache_slide_ && (slide_ == shared_cache_slide_));}

  bool GetIsDyld() {return is_dyld_;}

  // Difference between GetLoadAddress() and GetVMAddr()
  ptrdiff_t GetVMAddrSlide() const {return slide_;}

  // Size of the image
  mach_vm_size_t GetVMSize() const {return vmsize_;}

  // Returns the address of the locally cached __DATA,__crash_info section.
  // The vector will be empty if the image doesn't have a __crash_info
  // section. But even if the vector isn't empty, its contents may be "empty"
  // of useful data (see definition of crashreporter_annotations_t in
  // mach_vm_compat.h).
  mach_vm_address_t GetCrashInfo() const {
    return reinterpret_cast<mach_vm_address_t>(&crash_info_[0]);
  }

  // Size of the locally cached __DATA,__crash_info section. This will be zero
  // if the vector is empty. But even if it's non-zero, the __crash_info
  // section of which it's a copy may be empty of useful data.
  size_t GetCrashInfoSize() const {return crash_info_.size();}

  // Task owning this loaded image
  mach_port_t GetTask() {return task_;}

  // CPU type of the task and the image
  cpu_type_t GetCPUType() {return cpu_type_;}

  // CPU subtype of the image
  cpu_type_t GetCPUSubtype() {return cpu_subtype_;}

  // filetype from the Mach-O header.
  uint32_t GetFileType();

  // Return true if the task is a 64-bit architecture.
  bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }

  uint32_t GetVersion() {return version_;}
  // For sorting
  bool operator<(const DynamicImage &inInfo) {
    return GetLoadAddress() < inInfo.GetLoadAddress();
  }

  // Sanity checking
  bool IsValid() {return GetVMSize() != 0;}

 private:
  DynamicImage(const DynamicImage &);
  DynamicImage &operator=(const DynamicImage &);

  friend class DynamicImages;
  template<typename MachBits>
  friend bool FindTextSection(DynamicImage& image);
  template<typename MachBits>
  friend uint32_t GetFileTypeFromHeader(DynamicImage& image);

  // Initializes vmaddr_, vmsize_, and slide_
  void CalculateMemoryAndVersionInfo();

  const vector<uint8_t>   header_;        // our local copy of the header
  size_t                  header_size_;    // mach_header plus load commands
  uint64_t                load_address_;   // base address image is mapped into
  mach_vm_address_t       vmaddr_;
  mach_vm_size_t          vmsize_;
  ptrdiff_t               slide_;
  vector<uint8_t>         crash_info_;
  uint32_t                version_;        // Dylib version
  string                  file_path_;     // path dyld used to load the image
  uintptr_t               file_mod_date_;  // time_t of image file
  bool                    is_dyld_;        // Is image file dyld itself?

  mach_port_t             task_;
  cpu_type_t              cpu_type_;        // CPU type of task_ and image
  cpu_subtype_t           cpu_subtype_;     // CPU subtype of image
  ptrdiff_t               shared_cache_slide_; // Task's shared cache slide
};

//==============================================================================
// DynamicImageRef is just a simple wrapper for a pointer to
// DynamicImage.  The reason we use it instead of a simple typedef is so
// that we can use stl::sort() on a vector of DynamicImageRefs
// and simple class pointers can't implement operator<().
//
class DynamicImageRef {
 public:
  explicit DynamicImageRef(DynamicImage *inP) : p(inP) {}
  // The copy constructor is required by STL
  DynamicImageRef(const DynamicImageRef &inRef) : p(inRef.p) {}

  bool operator<(const DynamicImageRef &inRef) const {
    return (*const_cast<DynamicImageRef*>(this)->p)
      < (*const_cast<DynamicImageRef&>(inRef).p);
  }

  bool operator==(const DynamicImageRef &inInfo) const {
    return (*const_cast<DynamicImageRef*>(this)->p).GetLoadAddress() ==
        (*const_cast<DynamicImageRef&>(inInfo)).GetLoadAddress();
  }

  // Be just like DynamicImage*
  DynamicImage  *operator->() {return p;}
  operator DynamicImage*() {return p;}

 private:
  DynamicImage  *p;
};

// Helper function to deal with 32-bit/64-bit Mach-O differences.
class DynamicImages;
template<typename MachBits>
void ReadOneImageInfo(DynamicImages& images, uint64_t image_address,
                      uint64_t file_path_address, uint64_t file_mod_date,
                      uint64_t shared_cache_slide, bool is_dyld);
template<typename MachBits>
void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);

//==============================================================================
// An object of type DynamicImages may be created to allow introspection of
// an arbitrary task's dynamically loaded mach-o binaries.  This makes the
// assumption that the current task has send rights to the target task.
class DynamicImages {
 public:
  explicit DynamicImages(mach_port_t task);

  ~DynamicImages() {
    for (int i = 0; i < GetImageCount(); ++i) {
      delete image_list_[i];
    }
  }

  // Returns the number of dynamically loaded mach-o images.
  int GetImageCount() const {return static_cast<int>(image_list_.size());}

  // Returns an individual image.
  DynamicImage *GetImage(int i) {
    if (i < (int)image_list_.size()) {
      return image_list_[i];
    }
    return NULL;
  }

  // Returns the image corresponding to the main executable.
  DynamicImage *GetExecutableImage();
  int GetExecutableImageIndex();

  // Returns the task which we're looking at.
  mach_port_t GetTask() const {return task_;}

  // CPU type of the task
  cpu_type_t GetCPUType() {return cpu_type_;}

  // Return true if the task is a 64-bit architecture.
  bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }

  // Determine the CPU type of the task being dumped.
  static cpu_type_t DetermineTaskCPUType(task_t task);

  // Get the native CPU type of this task.
  static cpu_type_t GetNativeCPUType() {
#if defined(__i386__)
    return CPU_TYPE_I386;
#elif defined(__x86_64__)
    return CPU_TYPE_X86_64;
#elif defined(__ppc__)
    return CPU_TYPE_POWERPC;
#elif defined(__ppc64__)
    return CPU_TYPE_POWERPC64;
#elif defined(__arm__)
    return CPU_TYPE_ARM;
#elif defined(__aarch64__)
    return CPU_TYPE_ARM64;
#else
#error "GetNativeCPUType not implemented for this architecture"
#endif
  }

 private:
  template<typename MachBits>
  friend void ReadOneImageInfo(DynamicImages& images, uint64_t image_address,
                               uint64_t file_path_address, uint64_t file_mod_date,
                               uint64_t shared_cache_slide, bool is_dyld);
  template<typename MachBits>
  friend void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);

  bool IsOurTask() {return task_ == mach_task_self();}

  // Initialization
  void ReadImageInfoForTask();
  uint64_t GetDyldAllImageInfosPointer();

  mach_port_t              task_;
  cpu_type_t               cpu_type_;  // CPU type of task_
  vector<DynamicImageRef>  image_list_;
};

// Fill bytes with the contents of memory at a particular
// location in another task.
kern_return_t ReadTaskMemory(task_port_t target_task,
                             const uint64_t address,
                             size_t length,
                             vector<uint8_t> &bytes);

std::string ReadTaskString(task_port_t target_task,
                           const uint64_t address);

}   // namespace google_breakpad

#endif // CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__
