blob: b2be03c0fe7efdd14b6601a51777aa4020a3086d [file] [log] [blame]
// 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 "base/containers/heap_array.h"
#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) { \
auto specific = base::HeapArray<T>::Uninit(count); \
if (!T::ReadArrayInto( \
process_reader, address, specific.size(), specific.data())) { \
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