// 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. #ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_ #define CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_ #include #include #include #include #include #include #include #include #include #include "build/build_config.h" #include "util/misc/initialization_state_dcheck.h" #include "util/posix/process_info.h" #include "util/process/process_memory_mac.h" #if defined(ARCH_CPU_32_BIT) || \ (!defined(ARCH_CPU_ARM64) && \ __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_15) // There’s no 32-bit x86 environment on macOS 10.15 or later, and there’s no // 32-bit ARM environment for macOS at all. #define CRASHPAD_MAC_32_BIT_SUPPORT 1 #endif // ARCH_CPU_32_BIT || (!ARCH_CPU_ARM64 && DT < 10.15) namespace crashpad { class MachOImageReader; //! \brief Accesses information about another process, identified by a Mach //! task. class ProcessReaderMac { public: //! \brief Contains information about a thread that belongs to a task //! (process). struct Thread { #if defined(ARCH_CPU_X86_FAMILY) union ThreadContext { x86_thread_state64_t t64; x86_thread_state32_t t32; }; union FloatContext { x86_float_state64_t f64; x86_float_state32_t f32; }; union DebugContext { x86_debug_state64_t d64; x86_debug_state32_t d32; }; #elif defined(ARCH_CPU_ARM64) using ThreadContext = arm_thread_state64_t; using FloatContext = arm_neon_state64_t; using DebugContext = arm_debug_state64_t; #endif Thread(); ~Thread() {} ThreadContext thread_context; FloatContext float_context; DebugContext debug_context; std::string name; uint64_t id; mach_vm_address_t stack_region_address; mach_vm_size_t stack_region_size; mach_vm_address_t thread_specific_data_address; thread_t port; int suspend_count; int priority; }; //! \brief Contains information about a module loaded into a process. struct Module { Module(); ~Module(); //! \brief The pathname used to load the module from disk. std::string name; //! \brief An image reader for the module. //! //! The lifetime of this MachOImageReader is scoped to the lifetime of the //! ProcessReaderMac that created it. //! //! This field may be `nullptr` if a reader could not be created for the //! module. const MachOImageReader* reader; //! \brief The module’s timestamp. //! //! This field will be `0` if its value cannot be determined. It can only be //! determined for images that are loaded by dyld, so it will be `0` for the //! main executable and for dyld itself. time_t timestamp; }; ProcessReaderMac(); ProcessReaderMac(const ProcessReaderMac&) = delete; ProcessReaderMac& operator=(const ProcessReaderMac&) = delete; ~ProcessReaderMac(); //! \brief Initializes this object. This method must be called before any //! other. //! //! \param[in] task A send right to the target task’s task port. This object //! does not take ownership of the send right. //! //! \return `true` on success, indicating that this object will respond //! validly to further method calls. `false` on failure. On failure, no //! further method calls should be made. bool Initialize(task_t task); //! \return `true` if the target task is a 64-bit process. #if defined(CRASHPAD_MAC_32_BIT_SUPPORT) || DOXYGEN bool Is64Bit() const { return is_64_bit_; } #else // CRASHPAD_MAC_32_BIT_SUPPORT bool Is64Bit() const { return true; } #endif // CRASHPAD_MAC_32_BIT_SUPPORT //! \return The target task’s process ID. pid_t ProcessID() const { return process_info_.ProcessID(); } //! \return The target task’s parent process ID. pid_t ParentProcessID() const { return process_info_.ParentProcessID(); } //! \brief Determines the target process’ start time. //! //! \param[out] start_time The time that the process started. void StartTime(timeval* start_time) const; //! \brief Determines the target process’ execution time. //! //! \param[out] user_time The amount of time the process has executed code in //! user mode. //! \param[out] system_time The amount of time the process has executed code //! in system mode. //! //! \return `true` on success, `false` on failure, with a warning logged. On //! failure, \a user_time and \a system_time will be set to represent no //! time spent executing code in user or system mode. bool CPUTimes(timeval* user_time, timeval* system_time) const; //! \return Accesses the memory of the target task. const ProcessMemoryMac* Memory() const { return &process_memory_; } //! \return The threads that are in the task (process). The first element (at //! index `0`) corresponds to the main thread. const std::vector& Threads(); //! \return The modules loaded in the process. The first element (at index //! `0`) corresponds to the main executable, and the final element //! corresponds to the dynamic loader, dyld. const std::vector& Modules(); //! \brief Determines the location of the `dyld_all_image_infos` structure in //! the process’ address space. //! //! This function is an internal implementation detail of Modules(), and //! should not normally be used directly. It is exposed solely for use by test //! code. //! //! \param[out] all_image_info_size The size of the `dyld_all_image_infos` //! structure. Optional, may be `nullptr` if not required. //! //! \return The address of the `dyld_all_image_infos` structure in the //! process’ address space, with \a all_image_info_size set appropriately. //! On failure, returns `0` with a message logged. mach_vm_address_t DyldAllImageInfo(mach_vm_size_t* all_image_info_size); task_t task_; // weak private: //! Performs lazy initialization of the \a threads_ vector on behalf of //! Threads(). void InitializeThreads(); //! Performs lazy initialization of the \a modules_ vector on behalf of //! Modules(). void InitializeModules(); //! \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. mach_vm_address_t CalculateStackRegion(mach_vm_address_t stack_pointer, mach_vm_size_t* stack_region_size); //! \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(mach_vm_address_t* start_address, mach_vm_address_t* region_base, mach_vm_address_t* region_size, unsigned int user_tag); ProcessInfo process_info_; std::vector threads_; // owns send rights std::vector modules_; std::vector> module_readers_; ProcessMemoryMac process_memory_; InitializationStateDcheck initialized_; #if defined(CRASHPAD_MAC_32_BIT_SUPPORT) // This shadows a method of process_info_, but it’s accessed so frequently // that it’s given a first-class field to save a call and a few bit operations // on each access. bool is_64_bit_; #endif // CRASHPAD_MAC_32_BIT_SUPPORT bool initialized_threads_; bool initialized_modules_; }; } // namespace crashpad #endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_