// Copyright 2014 The Crashpad Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "snapshot/mac/process_types.h"

#include <stddef.h>
#include <string.h>
#include <uuid/uuid.h>

#include <iterator>
#include <memory>

#include "snapshot/mac/process_types/internal.h"
#include "util/process/process_memory_mac.h"

namespace crashpad {
namespace {

// Assign() is used by each flavor's ReadInto implementation to copy data from a
// specific struct to a generic struct. For fundamental types, the assignment
// operator suffices. For other types such as arrays, an explicit Assign
// specialization is needed, typically performing a copy.

template <typename DestinationType, typename SourceType>
inline void Assign(DestinationType* destination, const SourceType& source) {
  *destination = source;
}

template <typename Type>
inline void Assign(Type* destination, const Type& source) {
  memcpy(destination, &source, sizeof(source));
}

template <>
inline void Assign<process_types::internal::Reserved32_32Only64,
                   process_types::internal::Reserved32_32Only32>(
    process_types::internal::Reserved32_32Only64* destination,
    const process_types::internal::Reserved32_32Only32& source) {
  // Reserved32_32Only32 carries no data and has no storage in the 64-bit
  // structure.
}

template <>
inline void Assign<process_types::internal::Reserved32_64Only64,
                   process_types::internal::Reserved32_64Only32>(
    process_types::internal::Reserved32_64Only64* destination,
    const process_types::internal::Reserved32_64Only32& source) {
  // Reserved32_64Only32 carries no data.
  *destination = 0;
}

template <>
inline void Assign<process_types::internal::Reserved64_64Only64,
                   process_types::internal::Reserved64_64Only32>(
    process_types::internal::Reserved64_64Only64* destination,
    const process_types::internal::Reserved64_64Only32& source) {
  // Reserved64_64Only32 carries no data.
  *destination = 0;
}

using UInt32Array4 = uint32_t[4];
using UInt64Array4 = uint64_t[4];
template <>
inline void Assign<UInt64Array4, UInt32Array4>(UInt64Array4* destination,
                                               const UInt32Array4& source) {
  for (size_t index = 0; index < std::size(source); ++index) {
    (*destination)[index] = source[index];
  }
}

}  // namespace
}  // namespace crashpad

// Implement the generic crashpad::process_types::struct_name ReadInto(), which
// delegates to the templatized ReadIntoInternal(), which reads the specific
// struct type from the remote process and genericizes it. Also implement
// crashpad::process_types::internal::struct_name<> GenericizeInto(), which
// operates on each member in the struct.
#define PROCESS_TYPE_STRUCT_IMPLEMENT 1

#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)                            \
  namespace crashpad {                                                    \
  namespace process_types {                                               \
                                                                          \
  /* static */                                                            \
  size_t struct_name::ExpectedSize(ProcessReaderMac* process_reader) {    \
    if (!process_reader->Is64Bit()) {                                     \
      return internal::struct_name<internal::Traits32>::Size();           \
    } else {                                                              \
      return internal::struct_name<internal::Traits64>::Size();           \
    }                                                                     \
  }                                                                       \
                                                                          \
  /* static */                                                            \
  bool struct_name::ReadInto(ProcessReaderMac* process_reader,            \
                             mach_vm_address_t address,                   \
                             struct_name* generic) {                      \
    if (!process_reader->Is64Bit()) {                                     \
      return ReadIntoInternal<internal::struct_name<internal::Traits32>>( \
          process_reader, address, generic);                              \
    } else {                                                              \
      return ReadIntoInternal<internal::struct_name<internal::Traits64>>( \
          process_reader, address, generic);                              \
    }                                                                     \
  }                                                                       \
                                                                          \
  /* static */                                                            \
  template <typename T>                                                   \
  bool struct_name::ReadIntoInternal(ProcessReaderMac* process_reader,    \
                                     mach_vm_address_t address,           \
                                     struct_name* generic) {              \
    T specific;                                                           \
    if (!specific.Read(process_reader, address)) {                        \
      return false;                                                       \
    }                                                                     \
    specific.GenericizeInto(generic, &generic->size_);                    \
    return true;                                                          \
  }                                                                       \
                                                                          \
  namespace internal {                                                    \
                                                                          \
  template <typename Traits>                                              \
  void struct_name<Traits>::GenericizeInto(                               \
      process_types::struct_name* generic,                                \
      size_t* specific_size) {                                            \
    *specific_size = Size();

#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \
    Assign(&generic->member_name, member_name);

#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field)

#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)

#define PROCESS_TYPE_STRUCT_END(struct_name) \
  }                                          \
  }  /* namespace internal */                \
  }  /* namespace process_types */           \
  }  /* namespace crashpad */

#include "snapshot/mac/process_types/all.proctype"

#undef PROCESS_TYPE_STRUCT_BEGIN
#undef PROCESS_TYPE_STRUCT_MEMBER
#undef PROCESS_TYPE_STRUCT_VERSIONED
#undef PROCESS_TYPE_STRUCT_SIZED
#undef PROCESS_TYPE_STRUCT_END
#undef PROCESS_TYPE_STRUCT_IMPLEMENT

// Implement the specific crashpad::process_types::internal::struct_name<>
// ReadInto(). The default implementation simply reads the struct from the
// remote process. This is separated from other method implementations because
// some types may wish to provide custom readers. This can be done by guarding
// such types’ proctype definitions against this macro and providing custom
// implementations in snapshot/mac/process_types/custom.cc.
#define PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO 1

#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)                         \
  namespace crashpad {                                                 \
  namespace process_types {                                            \
  namespace internal {                                                 \
                                                                       \
  /* static */                                                         \
  template <typename Traits>                                           \
  bool struct_name<Traits>::ReadInto(ProcessReaderMac* process_reader, \
                                     mach_vm_address_t address,        \
                                     struct_name<Traits>* specific) {  \
    return process_reader->Memory()->Read(                             \
        address, sizeof(*specific), specific);                         \
  }                                                                    \
  } /* namespace internal */                                           \
  } /* namespace process_types */                                      \
  } /* namespace crashpad */

#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)

#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field)

#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)

#define PROCESS_TYPE_STRUCT_END(struct_name)

#include "snapshot/mac/process_types/all.proctype"

#undef PROCESS_TYPE_STRUCT_BEGIN
#undef PROCESS_TYPE_STRUCT_MEMBER
#undef PROCESS_TYPE_STRUCT_VERSIONED
#undef PROCESS_TYPE_STRUCT_SIZED
#undef PROCESS_TYPE_STRUCT_END
#undef PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO

// Implement the array operations. These are separated from other method
// implementations because some types are variable-length and are never stored
// as direct-access arrays. It would be incorrect to provide reader
// implementations for such types. Types that wish to suppress array operations
// can do so by guarding their proctype definitions against this macro.
#define PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY 1

#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)                                 \
  namespace crashpad {                                                         \
  namespace process_types {                                                    \
  namespace internal {                                                         \
                                                                               \
  /* static */                                                                 \
  template <typename Traits>                                                   \
  bool struct_name<Traits>::ReadArrayInto(ProcessReaderMac* process_reader,    \
                                          mach_vm_address_t address,           \
                                          size_t count,                        \
                                          struct_name<Traits>* specific) {     \
    return process_reader->Memory()->Read(                                     \
        address, count * sizeof(struct_name<Traits>), specific);               \
  }                                                                            \
                                                                               \
  } /* namespace internal */                                                   \
                                                                               \
  /* static */                                                                 \
  bool struct_name::ReadArrayInto(ProcessReaderMac* process_reader,            \
                                  mach_vm_address_t address,                   \
                                  size_t count,                                \
                                  struct_name* generic) {                      \
    if (!process_reader->Is64Bit()) {                                          \
      return ReadArrayIntoInternal<internal::struct_name<internal::Traits32>>( \
          process_reader, address, count, generic);                            \
    } else {                                                                   \
      return ReadArrayIntoInternal<internal::struct_name<internal::Traits64>>( \
          process_reader, address, count, generic);                            \
    }                                                                          \
    return true;                                                               \
  }                                                                            \
                                                                               \
  /* static */                                                                 \
  template <typename T>                                                        \
  bool struct_name::ReadArrayIntoInternal(ProcessReaderMac* process_reader,    \
                                          mach_vm_address_t address,           \
                                          size_t count,                        \
                                          struct_name* generic) {              \
    std::unique_ptr<T[]> specific(new T[count]);                               \
    if (!T::ReadArrayInto(process_reader, address, count, &specific[0])) {     \
      return false;                                                            \
    }                                                                          \
    for (size_t index = 0; index < count; ++index) {                           \
      specific[index].GenericizeInto(&generic[index], &generic[index].size_);  \
    }                                                                          \
    return true;                                                               \
  }                                                                            \
  } /* namespace process_types */                                              \
  } /* namespace crashpad */

#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)

#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field)

#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)

#define PROCESS_TYPE_STRUCT_END(struct_name)

#include "snapshot/mac/process_types/all.proctype"

#undef PROCESS_TYPE_STRUCT_BEGIN
#undef PROCESS_TYPE_STRUCT_MEMBER
#undef PROCESS_TYPE_STRUCT_VERSIONED
#undef PROCESS_TYPE_STRUCT_SIZED
#undef PROCESS_TYPE_STRUCT_END
#undef PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY

// Implement the generic crashpad::process_types::struct_name
// ExpectedSizeForVersion(), which delegates to the templatized
// ExpectedSizeForVersion(), which returns the expected size of a versioned
// structure given a version parameter. This is only implemented for structures
// that use PROCESS_TYPE_STRUCT_VERSIONED(), and implementations of the internal
// templatized functions must be provided in
// snapshot/mac/process_types/custom.cc.
#define PROCESS_TYPE_STRUCT_IMPLEMENT_VERSIONED 1

#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)

#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)

#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) \
  namespace crashpad {                                            \
  namespace process_types {                                       \
                                                                  \
  /* static */                                                    \
  size_t struct_name::ExpectedSizeForVersion(                     \
      ProcessReaderMac* process_reader,                           \
      decltype(struct_name::version_field) version) {             \
    if (!process_reader->Is64Bit()) {                             \
      return internal::struct_name<                               \
          internal::Traits32>::ExpectedSizeForVersion(version);   \
    } else {                                                      \
      return internal::struct_name<                               \
          internal::Traits64>::ExpectedSizeForVersion(version);   \
    }                                                             \
  }                                                               \
                                                                  \
  } /* namespace process_types */                                 \
  } /* namespace crashpad */

#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)

#define PROCESS_TYPE_STRUCT_END(struct_name)

#include "snapshot/mac/process_types/all.proctype"

#undef PROCESS_TYPE_STRUCT_BEGIN
#undef PROCESS_TYPE_STRUCT_MEMBER
#undef PROCESS_TYPE_STRUCT_VERSIONED
#undef PROCESS_TYPE_STRUCT_SIZED
#undef PROCESS_TYPE_STRUCT_END
#undef PROCESS_TYPE_STRUCT_IMPLEMENT_VERSIONED

// Implement the generic crashpad::process_types::struct_name MinimumSize() and
// its templatized equivalent. The generic version delegates to the templatized
// one, which returns the minimum size of a sized structure. This can be used to
// ensure that enough of a sized structure is available to interpret its size
// field. This is only implemented for structures that use
// PROCESS_TYPE_STRUCT_SIZED().
#define PROCESS_TYPE_STRUCT_IMPLEMENT_SIZED 1

#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)

#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)

#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field)

#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)             \
  namespace crashpad {                                                 \
  namespace process_types {                                            \
                                                                       \
  namespace internal {                                                 \
                                                                       \
  /* static */                                                         \
  template <typename Traits>                                           \
  size_t struct_name<Traits>::MinimumSize() {                          \
    return offsetof(struct_name<Traits>, size_field) +                 \
           sizeof(struct_name<Traits>::size_field);                    \
  }                                                                    \
                                                                       \
  /* Explicit instantiations of the above. */                          \
  template size_t struct_name<Traits32>::MinimumSize();                \
  template size_t struct_name<Traits64>::MinimumSize();                \
                                                                       \
  } /* namespace internal */                                           \
                                                                       \
  /* static */                                                         \
  size_t struct_name::MinimumSize(ProcessReaderMac* process_reader) {  \
    if (!process_reader->Is64Bit()) {                                  \
      return internal::struct_name<internal::Traits32>::MinimumSize(); \
    } else {                                                           \
      return internal::struct_name<internal::Traits64>::MinimumSize(); \
    }                                                                  \
  }                                                                    \
                                                                       \
  } /* namespace process_types */                                      \
  } /* namespace crashpad */

#define PROCESS_TYPE_STRUCT_END(struct_name)

#include "snapshot/mac/process_types/all.proctype"

#undef PROCESS_TYPE_STRUCT_BEGIN
#undef PROCESS_TYPE_STRUCT_MEMBER
#undef PROCESS_TYPE_STRUCT_VERSIONED
#undef PROCESS_TYPE_STRUCT_SIZED
#undef PROCESS_TYPE_STRUCT_END
#undef PROCESS_TYPE_STRUCT_IMPLEMENT_SIZED
