2021-08-11 12:53:57 -04:00
|
|
|
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
// 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 "client/ios_handler/in_process_intermediate_dump_handler.h"
|
|
|
|
|
|
|
|
|
|
#include <mach-o/dyld_images.h>
|
|
|
|
|
#include <mach-o/nlist.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
|
|
#include "base/cxx17_backports.h"
|
|
|
|
|
#include "build/build_config.h"
|
|
|
|
|
#include "snapshot/snapshot_constants.h"
|
|
|
|
|
#include "util/ios/ios_intermediate_dump_writer.h"
|
|
|
|
|
#include "util/ios/raw_logging.h"
|
|
|
|
|
#include "util/ios/scoped_vm_read.h"
|
|
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_X86_64)
|
|
|
|
|
const thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64;
|
|
|
|
|
const thread_state_flavor_t kFloatStateFlavor = x86_FLOAT_STATE64;
|
|
|
|
|
const thread_state_flavor_t kDebugStateFlavor = x86_DEBUG_STATE64;
|
|
|
|
|
using thread_state_type = x86_thread_state64_t;
|
|
|
|
|
#elif defined(ARCH_CPU_ARM64)
|
|
|
|
|
const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;
|
|
|
|
|
const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64;
|
|
|
|
|
const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64;
|
|
|
|
|
using thread_state_type = arm_thread_state64_t;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// From snapshot/mac/process_types/crashreporterclient.proctype
|
|
|
|
|
struct crashreporter_annotations_t {
|
|
|
|
|
uint64_t version;
|
|
|
|
|
uint64_t message;
|
|
|
|
|
uint64_t signature_string;
|
|
|
|
|
uint64_t backtrace;
|
|
|
|
|
uint64_t message2;
|
|
|
|
|
uint64_t thread;
|
|
|
|
|
uint64_t dialog_mode;
|
|
|
|
|
uint64_t abort_cause;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//! \brief Manage memory and ports after calling `task_threads`.
|
|
|
|
|
class ScopedTaskThreads {
|
|
|
|
|
public:
|
|
|
|
|
explicit ScopedTaskThreads(thread_act_array_t threads,
|
|
|
|
|
mach_msg_type_number_t thread_count)
|
|
|
|
|
: threads_(threads), thread_count_(thread_count) {}
|
|
|
|
|
|
2021-09-20 12:55:12 -07:00
|
|
|
|
ScopedTaskThreads(const ScopedTaskThreads&) = delete;
|
|
|
|
|
ScopedTaskThreads& operator=(const ScopedTaskThreads&) = delete;
|
|
|
|
|
|
2021-08-11 12:53:57 -04:00
|
|
|
|
~ScopedTaskThreads() {
|
|
|
|
|
for (uint32_t thread_index = 0; thread_index < thread_count_;
|
|
|
|
|
++thread_index) {
|
|
|
|
|
mach_port_deallocate(mach_task_self(), threads_[thread_index]);
|
|
|
|
|
}
|
|
|
|
|
vm_deallocate(mach_task_self(),
|
|
|
|
|
reinterpret_cast<vm_address_t>(threads_),
|
|
|
|
|
sizeof(thread_t) * thread_count_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
thread_act_array_t threads_;
|
|
|
|
|
mach_msg_type_number_t thread_count_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//! \brief Log \a key as a string.
|
|
|
|
|
void WriteError(IntermediateDumpKey key) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to write key");
|
|
|
|
|
switch (key) {
|
|
|
|
|
// clang-format off
|
|
|
|
|
#define CASE_KEY(Name, Value) \
|
|
|
|
|
case IntermediateDumpKey::Name: \
|
|
|
|
|
CRASHPAD_RAW_LOG(#Name); \
|
|
|
|
|
break;
|
|
|
|
|
INTERMEDIATE_DUMP_KEYS(CASE_KEY)
|
|
|
|
|
#undef CASE_KEY
|
|
|
|
|
// clang-format on
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! \brief Call AddProperty with raw error log.
|
|
|
|
|
//!
|
|
|
|
|
//! \param[in] writer The dump writer
|
|
|
|
|
//! \param[in] key The key to write.
|
|
|
|
|
//! \param[in] value Memory to be written.
|
|
|
|
|
//! \param[in] count Length of \a value.
|
|
|
|
|
template <typename T>
|
|
|
|
|
void WriteProperty(IOSIntermediateDumpWriter* writer,
|
|
|
|
|
IntermediateDumpKey key,
|
|
|
|
|
const T* value,
|
|
|
|
|
size_t count = 1) {
|
|
|
|
|
if (!writer->AddProperty(key, value, count))
|
|
|
|
|
WriteError(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! \brief Call AddPropertyBytes with raw error log.
|
|
|
|
|
//!
|
|
|
|
|
//! \param[in] writer The dump writer
|
|
|
|
|
//! \param[in] key The key to write.
|
|
|
|
|
//! \param[in] value Memory to be written.
|
|
|
|
|
//! \param[in] count Length of \a data.
|
|
|
|
|
void WritePropertyBytes(IOSIntermediateDumpWriter* writer,
|
|
|
|
|
IntermediateDumpKey key,
|
|
|
|
|
const void* value,
|
|
|
|
|
size_t value_length) {
|
|
|
|
|
if (!writer->AddPropertyBytes(key, value, value_length))
|
|
|
|
|
WriteError(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kern_return_t MachVMRegionRecurseDeepest(task_t task,
|
|
|
|
|
vm_address_t* address,
|
|
|
|
|
vm_size_t* size,
|
|
|
|
|
natural_t* depth,
|
|
|
|
|
vm_prot_t* protection,
|
|
|
|
|
unsigned int* user_tag) {
|
|
|
|
|
vm_region_submap_short_info_64 submap_info;
|
|
|
|
|
mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
|
|
|
|
|
while (true) {
|
|
|
|
|
// Note: vm_region_recurse() would be fine here, but it does not provide
|
|
|
|
|
// VM_REGION_SUBMAP_SHORT_INFO_COUNT.
|
|
|
|
|
kern_return_t kr = vm_region_recurse_64(
|
|
|
|
|
task,
|
|
|
|
|
address,
|
|
|
|
|
size,
|
|
|
|
|
depth,
|
|
|
|
|
reinterpret_cast<vm_region_recurse_info_t>(&submap_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "vm_region_recurse_64");
|
|
|
|
|
return kr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!submap_info.is_submap) {
|
|
|
|
|
*protection = submap_info.protection;
|
|
|
|
|
*user_tag = submap_info.user_tag;
|
|
|
|
|
return KERN_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++*depth;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! \brief Adjusts the region for the red zone, if the ABI requires one.
|
|
|
|
|
//!
|
|
|
|
|
//! This method performs red zone calculation for CalculateStackRegion(). Its
|
|
|
|
|
//! parameters are local variables used within that method, and may be
|
|
|
|
|
//! modified as needed.
|
|
|
|
|
//!
|
|
|
|
|
//! Where a red zone is required, the region of memory captured for a thread’s
|
|
|
|
|
//! stack will be extended to include the red zone below the stack pointer,
|
|
|
|
|
//! provided that such memory is mapped, readable, and has the correct user
|
|
|
|
|
//! tag value. If these conditions cannot be met fully, as much of the red
|
|
|
|
|
//! zone will be captured as is possible while meeting these conditions.
|
|
|
|
|
//!
|
|
|
|
|
//! \param[in,out] start_address The base address of the region to begin
|
|
|
|
|
//! capturing stack memory from. On entry, \a start_address is the stack
|
|
|
|
|
//! pointer. On return, \a start_address may be decreased to encompass a
|
|
|
|
|
//! red zone.
|
|
|
|
|
//! \param[in,out] region_base The base address of the region that contains
|
|
|
|
|
//! stack memory. This is distinct from \a start_address in that \a
|
|
|
|
|
//! region_base will be page-aligned. On entry, \a region_base is the
|
|
|
|
|
//! base address of a region that contains \a start_address. On return,
|
|
|
|
|
//! if \a start_address is decremented and is outside of the region
|
|
|
|
|
//! originally described by \a region_base, \a region_base will also be
|
|
|
|
|
//! decremented appropriately.
|
|
|
|
|
//! \param[in,out] region_size The size of the region that contains stack
|
|
|
|
|
//! memory. This region begins at \a region_base. On return, if \a
|
|
|
|
|
//! region_base is decremented, \a region_size will be incremented
|
|
|
|
|
//! appropriately.
|
|
|
|
|
//! \param[in] user_tag The Mach VM system’s user tag for the region described
|
|
|
|
|
//! by the initial values of \a region_base and \a region_size. The red
|
|
|
|
|
//! zone will only be allowed to extend out of the region described by
|
|
|
|
|
//! these initial values if the user tag is appropriate for stack memory
|
|
|
|
|
//! and the expanded region has the same user tag value.
|
|
|
|
|
void LocateRedZone(vm_address_t* const start_address,
|
|
|
|
|
vm_address_t* const region_base,
|
|
|
|
|
vm_address_t* const region_size,
|
|
|
|
|
const unsigned int user_tag) {
|
|
|
|
|
// x86_64 has a red zone. See AMD64 ABI 0.99.8,
|
|
|
|
|
// https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/uploads/01de35b2c8adc7545de52604cc45d942/x86-64-psABI-2021-05-20.pdf#page=23.
|
|
|
|
|
// section 3.2.2, “The Stack Frame”.
|
|
|
|
|
// So does ARM64,
|
|
|
|
|
// https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-Stacks-Red-Zone
|
|
|
|
|
// section "Respect the Stack’s Red Zone".
|
|
|
|
|
constexpr vm_size_t kRedZoneSize = 128;
|
|
|
|
|
vm_address_t red_zone_base =
|
|
|
|
|
*start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0;
|
|
|
|
|
bool red_zone_ok = false;
|
|
|
|
|
if (red_zone_base >= *region_base) {
|
|
|
|
|
// The red zone is within the region already discovered.
|
|
|
|
|
red_zone_ok = true;
|
|
|
|
|
} else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) {
|
|
|
|
|
// Probe to see if there’s a region immediately below the one already
|
|
|
|
|
// discovered.
|
|
|
|
|
vm_address_t red_zone_region_base = red_zone_base;
|
|
|
|
|
vm_size_t red_zone_region_size;
|
|
|
|
|
natural_t red_zone_depth = 0;
|
|
|
|
|
vm_prot_t red_zone_protection;
|
|
|
|
|
unsigned int red_zone_user_tag;
|
|
|
|
|
kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(),
|
|
|
|
|
&red_zone_region_base,
|
|
|
|
|
&red_zone_region_size,
|
|
|
|
|
&red_zone_depth,
|
|
|
|
|
&red_zone_protection,
|
|
|
|
|
&red_zone_user_tag);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
*start_address = *region_base;
|
|
|
|
|
} else if (red_zone_region_base + red_zone_region_size == *region_base &&
|
|
|
|
|
(red_zone_protection & VM_PROT_READ) != 0 &&
|
|
|
|
|
red_zone_user_tag == user_tag) {
|
|
|
|
|
// The region containing the red zone is immediately below the region
|
|
|
|
|
// already found, it’s readable (not the guard region), and it has the
|
|
|
|
|
// same user tag as the region already found, so merge them.
|
|
|
|
|
red_zone_ok = true;
|
|
|
|
|
*region_base -= red_zone_region_size;
|
|
|
|
|
*region_size += red_zone_region_size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (red_zone_ok) {
|
|
|
|
|
// Begin capturing from the base of the red zone (but not the entire
|
|
|
|
|
// region that encompasses the red zone).
|
|
|
|
|
*start_address = red_zone_base;
|
|
|
|
|
} else {
|
|
|
|
|
// The red zone would go lower into another region in memory, but no
|
|
|
|
|
// region was found. Memory can only be captured to an address as low as
|
|
|
|
|
// the base address of the region already found.
|
|
|
|
|
*start_address = *region_base;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! \brief Calculates the base address and size of the region used as a
|
|
|
|
|
//! thread’s stack.
|
|
|
|
|
//!
|
|
|
|
|
//! The region returned by this method may be formed by merging multiple
|
|
|
|
|
//! adjacent regions in a process’ memory map if appropriate. The base address
|
|
|
|
|
//! of the returned region may be lower than the \a stack_pointer passed in
|
|
|
|
|
//! when the ABI mandates a red zone below the stack pointer.
|
|
|
|
|
//!
|
|
|
|
|
//! \param[in] stack_pointer The stack pointer, referring to the top (lowest
|
|
|
|
|
//! address) of a thread’s stack.
|
|
|
|
|
//! \param[out] stack_region_size The size of the memory region used as the
|
|
|
|
|
//! thread’s stack.
|
|
|
|
|
//!
|
|
|
|
|
//! \return The base address (lowest address) of the memory region used as the
|
|
|
|
|
//! thread’s stack.
|
|
|
|
|
vm_address_t CalculateStackRegion(vm_address_t stack_pointer,
|
|
|
|
|
vm_size_t* stack_region_size) {
|
|
|
|
|
// For pthreads, it may be possible to compute the stack region based on the
|
|
|
|
|
// internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct
|
|
|
|
|
// for a thread can be located at TSD slot 0, or the known offsets of
|
|
|
|
|
// stackaddr and stacksize from the TSD area could be used.
|
|
|
|
|
vm_address_t region_base = stack_pointer;
|
|
|
|
|
vm_size_t region_size;
|
|
|
|
|
natural_t depth = 0;
|
|
|
|
|
vm_prot_t protection;
|
|
|
|
|
unsigned int user_tag;
|
|
|
|
|
kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(),
|
|
|
|
|
®ion_base,
|
|
|
|
|
®ion_size,
|
|
|
|
|
&depth,
|
|
|
|
|
&protection,
|
|
|
|
|
&user_tag);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "MachVMRegionRecurseDeepest");
|
|
|
|
|
*stack_region_size = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (region_base > stack_pointer) {
|
|
|
|
|
// There’s nothing mapped at the stack pointer’s address. Something may have
|
|
|
|
|
// trashed the stack pointer. Note that this shouldn’t happen for a normal
|
|
|
|
|
// stack guard region violation because the guard region is mapped but has
|
|
|
|
|
// VM_PROT_NONE protection.
|
|
|
|
|
*stack_region_size = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vm_address_t start_address = stack_pointer;
|
|
|
|
|
|
|
|
|
|
if ((protection & VM_PROT_READ) == 0) {
|
|
|
|
|
// If the region isn’t readable, the stack pointer probably points to the
|
|
|
|
|
// guard region. Don’t include it as part of the stack, and don’t include
|
|
|
|
|
// anything at any lower memory address. The code below may still possibly
|
|
|
|
|
// find the real stack region at a memory address higher than this region.
|
|
|
|
|
start_address = region_base + region_size;
|
|
|
|
|
} else {
|
|
|
|
|
// If the ABI requires a red zone, adjust the region to include it if
|
|
|
|
|
// possible.
|
|
|
|
|
LocateRedZone(&start_address, ®ion_base, ®ion_size, user_tag);
|
|
|
|
|
|
|
|
|
|
// Regardless of whether the ABI requires a red zone, capture up to
|
|
|
|
|
// kExtraCaptureSize additional bytes of stack, but only if present in the
|
|
|
|
|
// region that was already found.
|
|
|
|
|
constexpr vm_size_t kExtraCaptureSize = 128;
|
|
|
|
|
start_address = std::max(start_address >= kExtraCaptureSize
|
|
|
|
|
? start_address - kExtraCaptureSize
|
|
|
|
|
: start_address,
|
|
|
|
|
region_base);
|
|
|
|
|
|
|
|
|
|
// Align start_address to a 16-byte boundary, which can help readers by
|
|
|
|
|
// ensuring that data is aligned properly. This could page-align instead,
|
|
|
|
|
// but that might be wasteful.
|
|
|
|
|
constexpr vm_size_t kDesiredAlignment = 16;
|
|
|
|
|
start_address &= ~(kDesiredAlignment - 1);
|
|
|
|
|
DCHECK_GE(start_address, region_base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
region_size -= (start_address - region_base);
|
|
|
|
|
region_base = start_address;
|
|
|
|
|
|
|
|
|
|
vm_size_t total_region_size = region_size;
|
|
|
|
|
|
|
|
|
|
// The stack region may have gotten split up into multiple abutting regions.
|
|
|
|
|
// Try to coalesce them. This frequently happens for the main thread’s stack
|
|
|
|
|
// when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region
|
|
|
|
|
// is split up due to an mprotect() or vm_protect() call.
|
|
|
|
|
//
|
|
|
|
|
// Stack regions created by the kernel and the pthreads library will be marked
|
|
|
|
|
// with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions
|
|
|
|
|
// with the same tag should find an entire stack region. Checking that the
|
|
|
|
|
// protection on individual regions is not VM_PROT_NONE should guarantee that
|
|
|
|
|
// this algorithm doesn’t collect map entries belonging to another thread’s
|
|
|
|
|
// stack: well-behaved stacks (such as those created by the kernel and the
|
|
|
|
|
// pthreads library) have VM_PROT_NONE guard regions at their low-address
|
|
|
|
|
// ends.
|
|
|
|
|
//
|
|
|
|
|
// Other stack regions may not be so well-behaved and thus if user_tag is not
|
|
|
|
|
// VM_MEMORY_STACK, the single region that was found is used as-is without
|
|
|
|
|
// trying to merge it with other adjacent regions.
|
|
|
|
|
if (user_tag == VM_MEMORY_STACK) {
|
|
|
|
|
vm_address_t try_address = region_base;
|
|
|
|
|
vm_address_t original_try_address;
|
|
|
|
|
|
|
|
|
|
while (try_address += region_size,
|
|
|
|
|
original_try_address = try_address,
|
|
|
|
|
(kr = MachVMRegionRecurseDeepest(mach_task_self(),
|
|
|
|
|
&try_address,
|
|
|
|
|
®ion_size,
|
|
|
|
|
&depth,
|
|
|
|
|
&protection,
|
|
|
|
|
&user_tag) == KERN_SUCCESS) &&
|
|
|
|
|
try_address == original_try_address &&
|
|
|
|
|
(protection & VM_PROT_READ) != 0 &&
|
|
|
|
|
user_tag == VM_MEMORY_STACK) {
|
|
|
|
|
total_region_size += region_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) {
|
|
|
|
|
// Tolerate KERN_INVALID_ADDRESS because it will be returned when there
|
|
|
|
|
// are no more regions in the map at or above the specified |try_address|.
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "MachVMRegionRecurseDeepest");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*stack_region_size = total_region_size;
|
|
|
|
|
return region_base;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! \brief Write data around \a address to intermediate dump. Must be called
|
|
|
|
|
//! from within a ScopedArray.
|
|
|
|
|
void MaybeCaptureMemoryAround(IOSIntermediateDumpWriter* writer,
|
|
|
|
|
uint64_t address) {
|
|
|
|
|
constexpr uint64_t non_address_offset = 0x10000;
|
|
|
|
|
if (address < non_address_offset)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
constexpr uint64_t max_address = std::numeric_limits<uint64_t>::max();
|
|
|
|
|
|
|
|
|
|
if (address > max_address - non_address_offset)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
constexpr uint64_t kRegisterByteOffset = 128;
|
|
|
|
|
const uint64_t target = address - kRegisterByteOffset;
|
|
|
|
|
constexpr uint64_t size = 512;
|
|
|
|
|
static_assert(kRegisterByteOffset <= size / 2, "negative offset too large");
|
|
|
|
|
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArrayMap memory_region(writer);
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kThreadContextMemoryRegionAddress, &address);
|
|
|
|
|
// Don't use WritePropertyBytes, this one will fail regularly if |target|
|
|
|
|
|
// cannot be read.
|
|
|
|
|
writer->AddPropertyBytes(IntermediateDumpKey::kThreadContextMemoryRegionData,
|
|
|
|
|
reinterpret_cast<const void*>(target),
|
|
|
|
|
size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CaptureMemoryPointedToByThreadState(IOSIntermediateDumpWriter* writer,
|
|
|
|
|
thread_state_type thread_state) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArray memory_regions(
|
|
|
|
|
writer, IntermediateDumpKey::kThreadContextMemoryRegions);
|
|
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_X86_64)
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__rax);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__rbx);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__rcx);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__rdx);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__rdi);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__rsi);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__rbp);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__r8);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__r9);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__r10);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__r11);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__r12);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__r13);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__r14);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__r15);
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__rip);
|
|
|
|
|
#elif defined(ARCH_CPU_ARM_FAMILY)
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__pc);
|
|
|
|
|
for (size_t i = 0; i < base::size(thread_state.__x); ++i) {
|
|
|
|
|
MaybeCaptureMemoryAround(writer, thread_state.__x[i]);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WriteCrashpadSimpleAnnotationsDictionary(IOSIntermediateDumpWriter* writer,
|
|
|
|
|
CrashpadInfo* crashpad_info) {
|
|
|
|
|
if (!crashpad_info->simple_annotations())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ScopedVMRead<SimpleStringDictionary> simple_annotations;
|
|
|
|
|
if (!simple_annotations.Read(crashpad_info->simple_annotations())) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to read simple annotations.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const size_t count = simple_annotations->GetCount();
|
|
|
|
|
if (!count)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArray annotations_array(
|
|
|
|
|
writer, IntermediateDumpKey::kAnnotationsSimpleMap);
|
|
|
|
|
|
|
|
|
|
SimpleStringDictionary::Entry* entries =
|
|
|
|
|
reinterpret_cast<SimpleStringDictionary::Entry*>(
|
|
|
|
|
simple_annotations.get());
|
|
|
|
|
for (size_t index = 0; index < count; index++) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer);
|
|
|
|
|
const auto& entry = entries[index];
|
|
|
|
|
size_t key_length = strnlen(entry.key, sizeof(entry.key));
|
|
|
|
|
WritePropertyBytes(writer,
|
|
|
|
|
IntermediateDumpKey::kAnnotationName,
|
|
|
|
|
reinterpret_cast<const void*>(entry.key),
|
|
|
|
|
key_length);
|
|
|
|
|
size_t value_length = strnlen(entry.value, sizeof(entry.value));
|
|
|
|
|
WritePropertyBytes(writer,
|
|
|
|
|
IntermediateDumpKey::kAnnotationValue,
|
|
|
|
|
reinterpret_cast<const void*>(entry.value),
|
|
|
|
|
value_length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WriteAppleCrashReporterAnnotations(
|
|
|
|
|
IOSIntermediateDumpWriter* writer,
|
|
|
|
|
crashreporter_annotations_t* crash_info) {
|
|
|
|
|
// This number was totally made up out of nowhere, but it seems prudent to
|
|
|
|
|
// enforce some limit.
|
|
|
|
|
constexpr size_t kMaxMessageSize = 1024;
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedMap annotation_map(
|
|
|
|
|
writer, IntermediateDumpKey::kAnnotationsCrashInfo);
|
|
|
|
|
if (crash_info->message) {
|
|
|
|
|
const size_t message_len = strnlen(
|
|
|
|
|
reinterpret_cast<const char*>(crash_info->message), kMaxMessageSize);
|
|
|
|
|
WritePropertyBytes(writer,
|
|
|
|
|
IntermediateDumpKey::kAnnotationsCrashInfoMessage1,
|
|
|
|
|
reinterpret_cast<const void*>(crash_info->message),
|
|
|
|
|
message_len);
|
|
|
|
|
}
|
|
|
|
|
if (crash_info->message2) {
|
|
|
|
|
const size_t message_len = strnlen(
|
|
|
|
|
reinterpret_cast<const char*>(crash_info->message2), kMaxMessageSize);
|
|
|
|
|
WritePropertyBytes(writer,
|
|
|
|
|
IntermediateDumpKey::kAnnotationsCrashInfoMessage2,
|
|
|
|
|
reinterpret_cast<const void*>(crash_info->message2),
|
|
|
|
|
message_len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WriteDyldErrorStringAnnotation(
|
|
|
|
|
IOSIntermediateDumpWriter* writer,
|
|
|
|
|
const uint64_t address,
|
|
|
|
|
const symtab_command* symtab_command_ptr,
|
|
|
|
|
const dysymtab_command* dysymtab_command_ptr,
|
|
|
|
|
const segment_command_64* text_seg_ptr,
|
|
|
|
|
const segment_command_64* linkedit_seg_ptr,
|
|
|
|
|
vm_size_t slide) {
|
|
|
|
|
if (text_seg_ptr == nullptr || linkedit_seg_ptr == nullptr ||
|
|
|
|
|
symtab_command_ptr == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ScopedVMRead<symtab_command> symtab_command;
|
|
|
|
|
ScopedVMRead<dysymtab_command> dysymtab_command;
|
|
|
|
|
ScopedVMRead<segment_command_64> text_seg;
|
|
|
|
|
ScopedVMRead<segment_command_64> linkedit_seg;
|
|
|
|
|
if (!symtab_command.Read(symtab_command_ptr) ||
|
|
|
|
|
!text_seg.Read(text_seg_ptr) || !linkedit_seg.Read(linkedit_seg_ptr) ||
|
|
|
|
|
(dysymtab_command_ptr && !dysymtab_command.Read(dysymtab_command_ptr))) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to load dyld symbol table.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t file_slide =
|
|
|
|
|
(linkedit_seg->vmaddr - text_seg->vmaddr) - linkedit_seg->fileoff;
|
|
|
|
|
uint64_t strings = address + (symtab_command->stroff + file_slide);
|
|
|
|
|
nlist_64* symbol_ptr = reinterpret_cast<nlist_64*>(
|
|
|
|
|
address + (symtab_command->symoff + file_slide));
|
|
|
|
|
|
|
|
|
|
// If a dysymtab is present, use it to filter the symtab for just the
|
|
|
|
|
// portion used for extdefsym. If no dysymtab is present, the entire symtab
|
|
|
|
|
// will need to be consulted.
|
|
|
|
|
uint32_t symbol_count = symtab_command->nsyms;
|
|
|
|
|
if (dysymtab_command_ptr) {
|
|
|
|
|
symbol_ptr += dysymtab_command->iextdefsym;
|
|
|
|
|
symbol_count = dysymtab_command->nextdefsym;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < symbol_count; i++, symbol_ptr++) {
|
|
|
|
|
ScopedVMRead<nlist_64> symbol;
|
|
|
|
|
if (!symbol.Read(symbol_ptr)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to load dyld symbol table symbol.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!symbol->n_value)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ScopedVMRead<const char> symbol_name;
|
|
|
|
|
if (!symbol_name.Read(strings + symbol->n_un.n_strx)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to load dyld symbol name.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(symbol_name.get(), "_error_string") == 0) {
|
|
|
|
|
ScopedVMRead<const char> symbol_value;
|
|
|
|
|
if (!symbol_value.Read(symbol->n_value + slide)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to load dyld symbol value.");
|
|
|
|
|
}
|
|
|
|
|
// 1024 here is distinct from kMaxMessageSize above, because it refers to
|
|
|
|
|
// a precisely-sized buffer inside dyld.
|
|
|
|
|
const size_t value_len = strnlen(symbol_value.get(), 1024);
|
|
|
|
|
if (value_len) {
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kAnnotationsDyldErrorString,
|
|
|
|
|
symbol_value.get(),
|
|
|
|
|
value_len);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteHeader(
|
|
|
|
|
IOSIntermediateDumpWriter* writer) {
|
|
|
|
|
static constexpr uint8_t version = 1;
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kVersion, &version);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteProcessInfo(
|
|
|
|
|
IOSIntermediateDumpWriter* writer) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedMap process_map(
|
|
|
|
|
writer, IntermediateDumpKey::kProcessInfo);
|
|
|
|
|
|
|
|
|
|
timeval snapshot_time;
|
|
|
|
|
if (gettimeofday(&snapshot_time, nullptr) == 0) {
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kSnapshotTime, &snapshot_time);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG("gettimeofday");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Used by pid, parent pid and snapshot time.
|
|
|
|
|
kinfo_proc kern_proc_info;
|
|
|
|
|
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
|
|
|
|
|
size_t len = sizeof(kern_proc_info);
|
|
|
|
|
if (sysctl(mib, base::size(mib), &kern_proc_info, &len, nullptr, 0) == 0) {
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kPID, &kern_proc_info.kp_proc.p_pid);
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kParentPID,
|
|
|
|
|
&kern_proc_info.kp_eproc.e_ppid);
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kStartTime,
|
|
|
|
|
&kern_proc_info.kp_proc.p_starttime);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG("sysctl kern_proc_info");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Used by user time and system time.
|
|
|
|
|
mach_task_basic_info task_basic_info;
|
|
|
|
|
mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT;
|
|
|
|
|
kern_return_t kr = task_info(mach_task_self(),
|
|
|
|
|
MACH_TASK_BASIC_INFO,
|
|
|
|
|
reinterpret_cast<task_info_t>(&task_basic_info),
|
|
|
|
|
&task_basic_info_count);
|
|
|
|
|
if (kr == KERN_SUCCESS) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedMap task_info(
|
|
|
|
|
writer, IntermediateDumpKey::kTaskBasicInfo);
|
|
|
|
|
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kUserTime, &task_basic_info.user_time);
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kSystemTime, &task_basic_info.system_time);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG("task_info task_basic_info");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
task_thread_times_info_data_t task_thread_times;
|
|
|
|
|
mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT;
|
|
|
|
|
kr = task_info(mach_task_self(),
|
|
|
|
|
TASK_THREAD_TIMES_INFO,
|
|
|
|
|
reinterpret_cast<task_info_t>(&task_thread_times),
|
|
|
|
|
&task_thread_times_count);
|
|
|
|
|
if (kr == KERN_SUCCESS) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedMap task_thread_times_map(
|
|
|
|
|
writer, IntermediateDumpKey::kTaskThreadTimes);
|
|
|
|
|
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kUserTime, &task_thread_times.user_time);
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kSystemTime,
|
|
|
|
|
&task_thread_times.system_time);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG("task_info task_basic_info");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteSystemInfo(
|
|
|
|
|
IOSIntermediateDumpWriter* writer,
|
|
|
|
|
const IOSSystemDataCollector& system_data) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedMap system_map(
|
|
|
|
|
writer, IntermediateDumpKey::kSystemInfo);
|
|
|
|
|
|
|
|
|
|
const std::string& machine_description = system_data.MachineDescription();
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kMachineDescription,
|
|
|
|
|
machine_description.c_str(),
|
|
|
|
|
machine_description.length());
|
|
|
|
|
int os_version_major;
|
|
|
|
|
int os_version_minor;
|
|
|
|
|
int os_version_bugfix;
|
|
|
|
|
system_data.OSVersion(
|
|
|
|
|
&os_version_major, &os_version_minor, &os_version_bugfix);
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kOSVersionMajor, &os_version_major);
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kOSVersionMinor, &os_version_minor);
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kOSVersionBugfix, &os_version_bugfix);
|
|
|
|
|
const std::string& os_version_build = system_data.Build();
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kOSVersionBuild,
|
|
|
|
|
os_version_build.c_str(),
|
|
|
|
|
os_version_build.length());
|
|
|
|
|
|
|
|
|
|
int cpu_count = system_data.ProcessorCount();
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kCpuCount, &cpu_count);
|
|
|
|
|
const std::string& cpu_vendor = system_data.CPUVendor();
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kCpuVendor,
|
|
|
|
|
cpu_vendor.c_str(),
|
|
|
|
|
cpu_vendor.length());
|
|
|
|
|
|
|
|
|
|
bool has_daylight_saving_time = system_data.HasDaylightSavingTime();
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kHasDaylightSavingTime,
|
|
|
|
|
&has_daylight_saving_time);
|
|
|
|
|
bool is_daylight_saving_time = system_data.IsDaylightSavingTime();
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kIsDaylightSavingTime,
|
|
|
|
|
&is_daylight_saving_time);
|
|
|
|
|
int standard_offset_seconds = system_data.StandardOffsetSeconds();
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kStandardOffsetSeconds,
|
|
|
|
|
&standard_offset_seconds);
|
|
|
|
|
int daylight_offset_seconds = system_data.DaylightOffsetSeconds();
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kDaylightOffsetSeconds,
|
|
|
|
|
&daylight_offset_seconds);
|
|
|
|
|
const std::string& standard_name = system_data.StandardName();
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kStandardName,
|
|
|
|
|
standard_name.c_str(),
|
|
|
|
|
standard_name.length());
|
|
|
|
|
const std::string& daylight_name = system_data.DaylightName();
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kDaylightName,
|
|
|
|
|
daylight_name.c_str(),
|
|
|
|
|
daylight_name.length());
|
|
|
|
|
|
|
|
|
|
vm_size_t page_size;
|
|
|
|
|
host_page_size(mach_host_self(), &page_size);
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kPageSize, &page_size);
|
|
|
|
|
|
|
|
|
|
mach_msg_type_number_t host_size =
|
|
|
|
|
sizeof(vm_statistics_data_t) / sizeof(integer_t);
|
|
|
|
|
vm_statistics_data_t vm_stat;
|
|
|
|
|
kern_return_t kr = host_statistics(mach_host_self(),
|
|
|
|
|
HOST_VM_INFO,
|
|
|
|
|
reinterpret_cast<host_info_t>(&vm_stat),
|
|
|
|
|
&host_size);
|
|
|
|
|
if (kr == KERN_SUCCESS) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedMap vm_stat_map(
|
|
|
|
|
writer, IntermediateDumpKey::kVMStat);
|
|
|
|
|
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kActive, &vm_stat.active_count);
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kInactive, &vm_stat.inactive_count);
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kWired, &vm_stat.wire_count);
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kFree, &vm_stat.free_count);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG("host_statistics");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteThreadInfo(
|
|
|
|
|
IOSIntermediateDumpWriter* writer,
|
|
|
|
|
const uint64_t* frames,
|
|
|
|
|
const size_t num_frames) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArray thread_array(
|
|
|
|
|
writer, IntermediateDumpKey::kThreads);
|
|
|
|
|
|
|
|
|
|
// Exception thread ID.
|
2021-10-05 10:31:59 -07:00
|
|
|
|
#if defined(ARCH_CPU_ARM64)
|
2021-08-11 12:53:57 -04:00
|
|
|
|
uint64_t exception_thread_id = 0;
|
2021-10-05 10:31:59 -07:00
|
|
|
|
#endif
|
2021-08-11 12:53:57 -04:00
|
|
|
|
thread_identifier_info identifier_info;
|
|
|
|
|
mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
|
|
|
|
|
kern_return_t kr =
|
|
|
|
|
thread_info(mach_thread_self(),
|
|
|
|
|
THREAD_IDENTIFIER_INFO,
|
|
|
|
|
reinterpret_cast<thread_info_t>(&identifier_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr == KERN_SUCCESS) {
|
2021-10-05 10:31:59 -07:00
|
|
|
|
#if defined(ARCH_CPU_ARM64)
|
2021-08-11 12:53:57 -04:00
|
|
|
|
exception_thread_id = identifier_info.thread_id;
|
2021-10-05 10:31:59 -07:00
|
|
|
|
#endif
|
2021-08-11 12:53:57 -04:00
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_IDENTIFIER_INFO");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mach_msg_type_number_t thread_count = 0;
|
|
|
|
|
thread_act_array_t threads;
|
|
|
|
|
kr = task_threads(mach_task_self(), &threads, &thread_count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "task_threads");
|
|
|
|
|
}
|
|
|
|
|
ScopedTaskThreads threads_vm_owner(threads, thread_count);
|
|
|
|
|
|
|
|
|
|
for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArrayMap thread_map(writer);
|
|
|
|
|
thread_t thread = threads[thread_index];
|
|
|
|
|
|
|
|
|
|
thread_basic_info basic_info;
|
|
|
|
|
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
|
|
|
|
|
kr = thread_info(thread,
|
|
|
|
|
THREAD_BASIC_INFO,
|
|
|
|
|
reinterpret_cast<thread_info_t>(&basic_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr == KERN_SUCCESS) {
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kSuspendCount,
|
|
|
|
|
&basic_info.suspend_count);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_BASIC_INFO");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_precedence_policy precedence;
|
|
|
|
|
count = THREAD_PRECEDENCE_POLICY_COUNT;
|
|
|
|
|
boolean_t get_default = FALSE;
|
|
|
|
|
kr = thread_policy_get(thread,
|
|
|
|
|
THREAD_PRECEDENCE_POLICY,
|
|
|
|
|
reinterpret_cast<thread_policy_t>(&precedence),
|
|
|
|
|
&count,
|
|
|
|
|
&get_default);
|
|
|
|
|
if (kr == KERN_SUCCESS) {
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kPriority, &precedence.importance);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "thread_policy_get");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Thread ID.
|
2021-10-05 10:31:59 -07:00
|
|
|
|
#if defined(ARCH_CPU_ARM64)
|
2021-08-11 12:53:57 -04:00
|
|
|
|
uint64_t thread_id;
|
2021-10-05 10:31:59 -07:00
|
|
|
|
#endif
|
2021-08-11 12:53:57 -04:00
|
|
|
|
thread_identifier_info identifier_info;
|
|
|
|
|
count = THREAD_IDENTIFIER_INFO_COUNT;
|
|
|
|
|
kr = thread_info(thread,
|
|
|
|
|
THREAD_IDENTIFIER_INFO,
|
|
|
|
|
reinterpret_cast<thread_info_t>(&identifier_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr == KERN_SUCCESS) {
|
2021-10-05 10:31:59 -07:00
|
|
|
|
#if defined(ARCH_CPU_ARM64)
|
2021-08-11 12:53:57 -04:00
|
|
|
|
thread_id = identifier_info.thread_id;
|
2021-10-05 10:31:59 -07:00
|
|
|
|
#endif
|
2021-08-11 12:53:57 -04:00
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id);
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kThreadDataAddress,
|
|
|
|
|
&identifier_info.thread_handle);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_IDENTIFIER_INFO");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// thread_snapshot_ios_intermediate_dump::GenerateStackMemoryFromFrames is
|
|
|
|
|
// only implemented for arm64, so no x86_64 block here.
|
|
|
|
|
#if defined(ARCH_CPU_ARM64)
|
|
|
|
|
// For uncaught NSExceptions, use the frames passed from the system rather
|
|
|
|
|
// than the current thread state.
|
|
|
|
|
if (num_frames > 0 && exception_thread_id == thread_id) {
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kThreadUncaughtNSExceptionFrames,
|
|
|
|
|
frames,
|
|
|
|
|
num_frames);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_X86_64)
|
|
|
|
|
x86_thread_state64_t thread_state;
|
|
|
|
|
x86_float_state64_t float_state;
|
|
|
|
|
x86_debug_state64_t debug_state;
|
|
|
|
|
mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT;
|
|
|
|
|
mach_msg_type_number_t float_state_count = x86_FLOAT_STATE64_COUNT;
|
|
|
|
|
mach_msg_type_number_t debug_state_count = x86_DEBUG_STATE64_COUNT;
|
|
|
|
|
#elif defined(ARCH_CPU_ARM64)
|
|
|
|
|
arm_thread_state64_t thread_state;
|
|
|
|
|
arm_neon_state64_t float_state;
|
|
|
|
|
arm_debug_state64_t debug_state;
|
|
|
|
|
mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;
|
|
|
|
|
mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT;
|
|
|
|
|
mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
kern_return_t kr =
|
|
|
|
|
thread_get_state(thread,
|
|
|
|
|
kThreadStateFlavor,
|
|
|
|
|
reinterpret_cast<thread_state_t>(&thread_state),
|
|
|
|
|
&thread_state_count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "thread_get_state::kThreadStateFlavor");
|
|
|
|
|
}
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kThreadState, &thread_state);
|
|
|
|
|
|
|
|
|
|
kr = thread_get_state(thread,
|
|
|
|
|
kFloatStateFlavor,
|
|
|
|
|
reinterpret_cast<thread_state_t>(&float_state),
|
|
|
|
|
&float_state_count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "thread_get_state::kFloatStateFlavor");
|
|
|
|
|
}
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kFloatState, &float_state);
|
|
|
|
|
|
|
|
|
|
kr = thread_get_state(thread,
|
|
|
|
|
kDebugStateFlavor,
|
|
|
|
|
reinterpret_cast<thread_state_t>(&debug_state),
|
|
|
|
|
&debug_state_count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "thread_get_state::kDebugStateFlavor");
|
|
|
|
|
}
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kDebugState, &debug_state);
|
|
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_X86_64)
|
|
|
|
|
vm_address_t stack_pointer = thread_state.__rsp;
|
|
|
|
|
#elif defined(ARCH_CPU_ARM64)
|
|
|
|
|
vm_address_t stack_pointer = thread_state.__sp;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
vm_size_t stack_region_size;
|
|
|
|
|
const vm_address_t stack_region_address =
|
|
|
|
|
CalculateStackRegion(stack_pointer, &stack_region_size);
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kStackRegionAddress,
|
|
|
|
|
&stack_region_address);
|
|
|
|
|
WritePropertyBytes(writer,
|
|
|
|
|
IntermediateDumpKey::kStackRegionData,
|
|
|
|
|
reinterpret_cast<const void*>(stack_region_address),
|
|
|
|
|
stack_region_size);
|
|
|
|
|
|
|
|
|
|
// Grab extra memory from context.
|
|
|
|
|
CaptureMemoryPointedToByThreadState(writer, thread_state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteModuleInfo(
|
|
|
|
|
IOSIntermediateDumpWriter* writer) {
|
|
|
|
|
#ifndef ARCH_CPU_64_BITS
|
|
|
|
|
#error Only 64-bit Mach-O is supported
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArray module_array(
|
|
|
|
|
writer, IntermediateDumpKey::kModules);
|
|
|
|
|
|
|
|
|
|
task_dyld_info_data_t dyld_info;
|
|
|
|
|
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
|
|
|
|
|
kern_return_t kr = task_info(mach_task_self(),
|
|
|
|
|
TASK_DYLD_INFO,
|
|
|
|
|
reinterpret_cast<task_info_t>(&dyld_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "task_info");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ScopedVMRead<dyld_all_image_infos> image_infos;
|
|
|
|
|
if (!image_infos.Read(dyld_info.all_image_info_addr)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to dyld_info.all_image_info_addr");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t image_count = image_infos->infoArrayCount;
|
|
|
|
|
const dyld_image_info* image_array = image_infos->infoArray;
|
|
|
|
|
for (uint32_t image_index = 0; image_index < image_count; ++image_index) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArrayMap modules(writer);
|
|
|
|
|
ScopedVMRead<dyld_image_info> image;
|
|
|
|
|
if (!image.Read(&image_array[image_index])) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to dyld_image_info");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kName,
|
|
|
|
|
image->imageFilePath,
|
|
|
|
|
strlen(image->imageFilePath));
|
|
|
|
|
uint64_t address = FromPointerCast<uint64_t>(image->imageLoadAddress);
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kAddress, &address);
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kTimestamp, &image->imageFileModDate);
|
|
|
|
|
WriteModuleInfoAtAddress(writer, address, false /*is_dyld=false*/);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArrayMap modules(writer);
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kName, image_infos->dyldPath);
|
|
|
|
|
uint64_t address =
|
|
|
|
|
FromPointerCast<uint64_t>(image_infos->dyldImageLoadAddress);
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kAddress, &address);
|
|
|
|
|
WriteModuleInfoAtAddress(writer, address, true /*is_dyld=true*/);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteExceptionFromSignal(
|
|
|
|
|
IOSIntermediateDumpWriter* writer,
|
|
|
|
|
const IOSSystemDataCollector& system_data,
|
|
|
|
|
siginfo_t* siginfo,
|
|
|
|
|
ucontext_t* context) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedMap signal_exception_map(
|
|
|
|
|
writer, IntermediateDumpKey::kSignalException);
|
|
|
|
|
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kSignalNumber, &siginfo->si_signo);
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kSignalCode, &siginfo->si_code);
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kSignalAddress, &siginfo->si_addr);
|
|
|
|
|
#if defined(ARCH_CPU_X86_64)
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kThreadState, &context->uc_mcontext->__ss);
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kFloatState, &context->uc_mcontext->__fs);
|
|
|
|
|
#elif defined(ARCH_CPU_ARM64)
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kThreadState, &context->uc_mcontext->__ss);
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kFloatState, &context->uc_mcontext->__ns);
|
|
|
|
|
#else
|
|
|
|
|
#error Port to your CPU architecture
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Thread ID.
|
|
|
|
|
thread_identifier_info identifier_info;
|
|
|
|
|
mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
|
|
|
|
|
kern_return_t kr =
|
|
|
|
|
thread_info(mach_thread_self(),
|
|
|
|
|
THREAD_IDENTIFIER_INFO,
|
|
|
|
|
reinterpret_cast<thread_info_t>(&identifier_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr == KERN_SUCCESS) {
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::self");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteExceptionFromMachException(
|
|
|
|
|
IOSIntermediateDumpWriter* writer,
|
|
|
|
|
exception_behavior_t behavior,
|
|
|
|
|
thread_t exception_thread,
|
|
|
|
|
exception_type_t exception,
|
|
|
|
|
const mach_exception_data_type_t* code,
|
|
|
|
|
mach_msg_type_number_t code_count,
|
|
|
|
|
thread_state_flavor_t flavor,
|
|
|
|
|
ConstThreadState state,
|
|
|
|
|
mach_msg_type_number_t state_count) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedMap mach_exception_map(
|
|
|
|
|
writer, IntermediateDumpKey::kMachException);
|
|
|
|
|
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kException, &exception);
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kCodes, code, code_count);
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kFlavor, &flavor);
|
|
|
|
|
WritePropertyBytes(writer,
|
|
|
|
|
IntermediateDumpKey::kState,
|
|
|
|
|
state,
|
|
|
|
|
state_count * sizeof(uint32_t));
|
|
|
|
|
|
|
|
|
|
thread_identifier_info identifier_info;
|
|
|
|
|
mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
|
|
|
|
|
kern_return_t kr =
|
|
|
|
|
thread_info(exception_thread,
|
|
|
|
|
THREAD_IDENTIFIER_INFO,
|
|
|
|
|
reinterpret_cast<thread_info_t>(&identifier_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr == KERN_SUCCESS) {
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "thread_info");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteExceptionFromNSException(
|
|
|
|
|
IOSIntermediateDumpWriter* writer) {
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedMap nsexception_map(
|
|
|
|
|
writer, IntermediateDumpKey::kNSException);
|
|
|
|
|
|
|
|
|
|
thread_identifier_info identifier_info;
|
|
|
|
|
mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
|
|
|
|
|
kern_return_t kr =
|
|
|
|
|
thread_info(mach_thread_self(),
|
|
|
|
|
THREAD_IDENTIFIER_INFO,
|
|
|
|
|
reinterpret_cast<thread_info_t>(&identifier_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr == KERN_SUCCESS) {
|
|
|
|
|
WriteProperty(
|
|
|
|
|
writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id);
|
|
|
|
|
} else {
|
|
|
|
|
CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::self");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteModuleInfoAtAddress(
|
|
|
|
|
IOSIntermediateDumpWriter* writer,
|
|
|
|
|
uint64_t address,
|
|
|
|
|
bool is_dyld) {
|
|
|
|
|
ScopedVMRead<mach_header_64> header;
|
|
|
|
|
if (!header.Read(address) || header->magic != MH_MAGIC_64) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Invalid module header");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const load_command* command_ptr = reinterpret_cast<const load_command*>(
|
|
|
|
|
reinterpret_cast<const mach_header_64*>(address) + 1);
|
|
|
|
|
|
|
|
|
|
ScopedVMRead<load_command> command;
|
|
|
|
|
if (!command.Read(command_ptr)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Invalid module command");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure that the basic load command structure doesn’t overflow the
|
|
|
|
|
// space allotted for load commands, as well as iterating through ncmds.
|
|
|
|
|
vm_size_t slide = 0;
|
|
|
|
|
const symtab_command* symtab_command = nullptr;
|
|
|
|
|
const dysymtab_command* dysymtab_command = nullptr;
|
|
|
|
|
const segment_command_64* linkedit_seg = nullptr;
|
|
|
|
|
const segment_command_64* text_seg = nullptr;
|
|
|
|
|
for (uint32_t cmd_index = 0, cumulative_cmd_size = 0;
|
|
|
|
|
cmd_index <= header->ncmds && cumulative_cmd_size < header->sizeofcmds;
|
|
|
|
|
++cmd_index, cumulative_cmd_size += command->cmdsize) {
|
|
|
|
|
if (command->cmd == LC_SEGMENT_64) {
|
|
|
|
|
ScopedVMRead<segment_command_64> segment;
|
|
|
|
|
if (!segment.Read(command_ptr)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Invalid LC_SEGMENT_64 segment");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const segment_command_64* segment_ptr =
|
|
|
|
|
reinterpret_cast<const segment_command_64*>(command_ptr);
|
|
|
|
|
if (strcmp(segment->segname, SEG_TEXT) == 0) {
|
|
|
|
|
text_seg = segment_ptr;
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kSize, &segment->vmsize);
|
|
|
|
|
slide = address - segment->vmaddr;
|
|
|
|
|
} else if (strcmp(segment->segname, SEG_DATA) == 0) {
|
|
|
|
|
WriteDataSegmentAnnotations(writer, segment_ptr, slide);
|
|
|
|
|
} else if (strcmp(segment->segname, SEG_LINKEDIT) == 0) {
|
|
|
|
|
linkedit_seg = segment_ptr;
|
|
|
|
|
}
|
|
|
|
|
} else if (command->cmd == LC_SYMTAB) {
|
|
|
|
|
symtab_command =
|
|
|
|
|
reinterpret_cast<const struct symtab_command*>(command_ptr);
|
|
|
|
|
} else if (command->cmd == LC_DYSYMTAB) {
|
|
|
|
|
dysymtab_command =
|
|
|
|
|
reinterpret_cast<const struct dysymtab_command*>(command_ptr);
|
|
|
|
|
} else if (command->cmd == LC_ID_DYLIB) {
|
|
|
|
|
ScopedVMRead<dylib_command> dylib;
|
|
|
|
|
if (!dylib.Read(command_ptr)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Invalid LC_ID_DYLIB segment");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kDylibCurrentVersion,
|
|
|
|
|
&dylib->dylib.current_version);
|
|
|
|
|
} else if (command->cmd == LC_SOURCE_VERSION) {
|
|
|
|
|
ScopedVMRead<source_version_command> source_version;
|
|
|
|
|
if (!source_version.Read(command_ptr)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Invalid LC_SOURCE_VERSION segment");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
WriteProperty(writer,
|
|
|
|
|
IntermediateDumpKey::kSourceVersion,
|
|
|
|
|
&source_version->version);
|
|
|
|
|
} else if (command->cmd == LC_UUID) {
|
|
|
|
|
ScopedVMRead<uuid_command> uuid;
|
|
|
|
|
if (!uuid.Read(command_ptr)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Invalid LC_UUID segment");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kUUID, &uuid->uuid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
command_ptr = reinterpret_cast<const load_command*>(
|
|
|
|
|
reinterpret_cast<const uint8_t*>(command_ptr) + command->cmdsize);
|
|
|
|
|
if (!command.Read(command_ptr)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Invalid module command");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WriteProperty(writer, IntermediateDumpKey::kFileType, &header->filetype);
|
|
|
|
|
|
|
|
|
|
if (is_dyld && header->filetype == MH_DYLINKER) {
|
|
|
|
|
WriteDyldErrorStringAnnotation(writer,
|
|
|
|
|
address,
|
|
|
|
|
symtab_command,
|
|
|
|
|
dysymtab_command,
|
|
|
|
|
text_seg,
|
|
|
|
|
linkedit_seg,
|
|
|
|
|
slide);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteDataSegmentAnnotations(
|
|
|
|
|
IOSIntermediateDumpWriter* writer,
|
|
|
|
|
const segment_command_64* segment_ptr,
|
|
|
|
|
vm_size_t slide) {
|
|
|
|
|
ScopedVMRead<segment_command_64> segment;
|
|
|
|
|
if (!segment.Read(segment_ptr)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to read SEG_DATA.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const section_64* section_ptr = reinterpret_cast<const section_64*>(
|
|
|
|
|
reinterpret_cast<uint64_t>(segment_ptr) + sizeof(segment_command_64));
|
|
|
|
|
for (uint32_t sect_index = 0; sect_index <= segment->nsects; ++sect_index) {
|
|
|
|
|
ScopedVMRead<section_64> section;
|
|
|
|
|
if (!section.Read(section_ptr)) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to read SEG_DATA section.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(section->sectname, "crashpad_info") == 0) {
|
|
|
|
|
ScopedVMRead<CrashpadInfo> crashpad_info;
|
|
|
|
|
if (crashpad_info.Read(section->addr + slide) &&
|
|
|
|
|
crashpad_info->size() == sizeof(CrashpadInfo) &&
|
|
|
|
|
crashpad_info->signature() == CrashpadInfo::kSignature &&
|
|
|
|
|
crashpad_info->version() == 1) {
|
|
|
|
|
WriteCrashpadAnnotationsList(writer, crashpad_info.get());
|
|
|
|
|
WriteCrashpadSimpleAnnotationsDictionary(writer, crashpad_info.get());
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(section->sectname, "__crash_info") == 0) {
|
|
|
|
|
ScopedVMRead<crashreporter_annotations_t> crash_info;
|
|
|
|
|
if (!crash_info.Read(section->addr + slide) ||
|
|
|
|
|
(crash_info->version != 4 && crash_info->version != 5)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
WriteAppleCrashReporterAnnotations(writer, crash_info.get());
|
|
|
|
|
}
|
|
|
|
|
section_ptr = reinterpret_cast<const section_64*>(
|
|
|
|
|
reinterpret_cast<uint64_t>(section_ptr) + sizeof(section_64));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InProcessIntermediateDumpHandler::WriteCrashpadAnnotationsList(
|
|
|
|
|
IOSIntermediateDumpWriter* writer,
|
|
|
|
|
CrashpadInfo* crashpad_info) {
|
|
|
|
|
if (!crashpad_info->annotations_list()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ScopedVMRead<AnnotationList> annotation_list;
|
|
|
|
|
if (!annotation_list.Read(crashpad_info->annotations_list())) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to read annotations list object");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArray annotations_array(
|
|
|
|
|
writer, IntermediateDumpKey::kAnnotationObjects);
|
|
|
|
|
ScopedVMRead<Annotation> current;
|
|
|
|
|
if (!current.Read(annotation_list->head())) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to read annotation");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t index = 0;
|
|
|
|
|
current->link_node() != annotation_list.get()->tail_pointer() &&
|
|
|
|
|
index < kMaxNumberOfAnnotations;
|
|
|
|
|
++index) {
|
|
|
|
|
ScopedVMRead<Annotation> node;
|
|
|
|
|
if (!node.Read(current->link_node())) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Unable to read annotation");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
current.Read(current->link_node());
|
|
|
|
|
|
|
|
|
|
if (node->size() == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (node->size() > Annotation::kValueMaxSize) {
|
|
|
|
|
CRASHPAD_RAW_LOG("Incorrect annotation length");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer);
|
|
|
|
|
const size_t name_len = strnlen(reinterpret_cast<const char*>(node->name()),
|
|
|
|
|
Annotation::kNameMaxLength);
|
|
|
|
|
WritePropertyBytes(writer,
|
|
|
|
|
IntermediateDumpKey::kAnnotationName,
|
|
|
|
|
reinterpret_cast<const void*>(node->name()),
|
|
|
|
|
name_len);
|
|
|
|
|
WritePropertyBytes(writer,
|
|
|
|
|
IntermediateDumpKey::kAnnotationValue,
|
|
|
|
|
reinterpret_cast<const void*>(node->value()),
|
|
|
|
|
node->size());
|
|
|
|
|
Annotation::Type type = node->type();
|
|
|
|
|
WritePropertyBytes(writer,
|
|
|
|
|
IntermediateDumpKey::kAnnotationType,
|
|
|
|
|
reinterpret_cast<const void*>(&type),
|
|
|
|
|
sizeof(type));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace internal
|
|
|
|
|
} // namespace crashpad
|