1//===-- MachProcess.cpp -----------------------------------------*- C++ -*-===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8// 9// Created by Greg Clayton on 6/15/07. 10// 11//===----------------------------------------------------------------------===// 12 13#include "DNB.h" 14#include "MacOSX/CFUtils.h" 15#include "SysSignal.h" 16#include <dlfcn.h> 17#include <inttypes.h> 18#include <mach-o/loader.h> 19#include <mach/mach.h> 20#include <mach/task.h> 21#include <pthread.h> 22#include <signal.h> 23#include <spawn.h> 24#include <sys/fcntl.h> 25#include <sys/ptrace.h> 26#include <sys/stat.h> 27#include <sys/sysctl.h> 28#include <sys/time.h> 29#include <sys/types.h> 30#include <unistd.h> 31#include <uuid/uuid.h> 32 33#include <algorithm> 34#include <chrono> 35#include <map> 36#include <unordered_set> 37 38#include <TargetConditionals.h> 39#import <Foundation/Foundation.h> 40 41#include "DNBDataRef.h" 42#include "DNBLog.h" 43#include "DNBThreadResumeActions.h" 44#include "DNBTimer.h" 45#include "MachProcess.h" 46#include "PseudoTerminal.h" 47 48#include "CFBundle.h" 49#include "CFString.h" 50 51#ifndef PLATFORM_BRIDGEOS 52#define PLATFORM_BRIDGEOS 5 53#endif 54 55#ifndef PLATFORM_MACCATALYST 56#define PLATFORM_MACCATALYST 6 57#endif 58 59#ifndef PLATFORM_IOSSIMULATOR 60#define PLATFORM_IOSSIMULATOR 7 61#endif 62 63#ifndef PLATFORM_TVOSSIMULATOR 64#define PLATFORM_TVOSSIMULATOR 8 65#endif 66 67#ifndef PLATFORM_WATCHOSSIMULATOR 68#define PLATFORM_WATCHOSSIMULATOR 9 69#endif 70 71#ifndef PLATFORM_DRIVERKIT 72#define PLATFORM_DRIVERKIT 10 73#endif 74 75#ifndef PLATFORM_VISIONOS 76#define PLATFORM_VISIONOS 11 77#endif 78 79#ifndef PLATFORM_VISIONOSSIMULATOR 80#define PLATFORM_VISIONOSSIMULATOR 12 81#endif 82 83#ifdef WITH_SPRINGBOARD 84 85#include <CoreFoundation/CoreFoundation.h> 86#include <SpringBoardServices/SBSWatchdogAssertion.h> 87#include <SpringBoardServices/SpringBoardServer.h> 88 89#endif // WITH_SPRINGBOARD 90 91#if WITH_CAROUSEL 92// For definition of CSLSOpenApplicationOptionForClockKit. 93#include <CarouselServices/CSLSOpenApplicationOptions.h> 94#endif // WITH_CAROUSEL 95 96#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) || defined(WITH_FBS) 97// This returns a CFRetained pointer to the Bundle ID for app_bundle_path, 98// or NULL if there was some problem getting the bundle id. 99static CFStringRef CopyBundleIDForPath(const char *app_bundle_path, 100 DNBError &err_str); 101#endif 102 103#if defined(WITH_BKS) || defined(WITH_FBS) 104#import <Foundation/Foundation.h> 105static const int OPEN_APPLICATION_TIMEOUT_ERROR = 111; 106typedef void (*SetErrorFunction)(NSInteger, std::string, DNBError &); 107typedef bool (*CallOpenApplicationFunction)(NSString *bundleIDNSStr, 108 NSDictionary *options, 109 DNBError &error, pid_t *return_pid); 110 111// This function runs the BKSSystemService (or FBSSystemService) method 112// openApplication:options:clientPort:withResult, 113// messaging the app passed in bundleIDNSStr. 114// The function should be run inside of an NSAutoReleasePool. 115// 116// It will use the "options" dictionary passed in, and fill the error passed in 117// if there is an error. 118// If return_pid is not NULL, we'll fetch the pid that was made for the 119// bundleID. 120// If bundleIDNSStr is NULL, then the system application will be messaged. 121 122template <typename OpenFlavor, typename ErrorFlavor, 123 ErrorFlavor no_error_enum_value, SetErrorFunction error_function> 124static bool CallBoardSystemServiceOpenApplication(NSString *bundleIDNSStr, 125 NSDictionary *options, 126 DNBError &error, 127 pid_t *return_pid) { 128 // Now make our systemService: 129 OpenFlavor *system_service = [[OpenFlavor alloc] init]; 130 131 if (bundleIDNSStr == nil) { 132 bundleIDNSStr = [system_service systemApplicationBundleIdentifier]; 133 if (bundleIDNSStr == nil) { 134 // Okay, no system app... 135 error.SetErrorString("No system application to message."); 136 return false; 137 } 138 } 139 140 mach_port_t client_port = [system_service createClientPort]; 141 __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 142 __block ErrorFlavor open_app_error = no_error_enum_value; 143 __block std::string open_app_error_string; 144 bool wants_pid = (return_pid != NULL); 145 __block pid_t pid_in_block; 146 147 const char *cstr = [bundleIDNSStr UTF8String]; 148 if (!cstr) 149 cstr = "<Unknown Bundle ID>"; 150 151 NSString *description = [options description]; 152 DNBLog("[LaunchAttach] START (%d) templated *Board launcher: app lunch " 153 "request for " 154 "'%s' - options:\n%s", 155 getpid(), cstr, [description UTF8String]); 156 [system_service 157 openApplication:bundleIDNSStr 158 options:options 159 clientPort:client_port 160 withResult:^(NSError *bks_error) { 161 // The system service will cleanup the client port we created for 162 // us. 163 if (bks_error) 164 open_app_error = (ErrorFlavor)[bks_error code]; 165 166 if (open_app_error == no_error_enum_value) { 167 if (wants_pid) { 168 pid_in_block = 169 [system_service pidForApplication:bundleIDNSStr]; 170 DNBLog("[LaunchAttach] In completion handler, got pid for " 171 "bundle id " 172 "'%s', pid: %d.", 173 cstr, pid_in_block); 174 } else { 175 DNBLog("[LaunchAttach] In completion handler, launch was " 176 "successful, " 177 "debugserver did not ask for the pid"); 178 } 179 } else { 180 const char *error_str = 181 [(NSString *)[bks_error localizedDescription] UTF8String]; 182 if (error_str) { 183 open_app_error_string = error_str; 184 DNBLogError( 185 "[LaunchAttach] END (%d) In app launch attempt, got error " 186 "localizedDescription '%s'.", 187 getpid(), error_str); 188 const char *obj_desc = 189 [NSString stringWithFormat:@"%@", bks_error].UTF8String; 190 DNBLogError( 191 "[LaunchAttach] END (%d) In app launch attempt, got error " 192 "NSError object description: '%s'.", 193 getpid(), obj_desc); 194 } 195 DNBLogThreadedIf(LOG_PROCESS, 196 "In completion handler for send " 197 "event, got error \"%s\"(%ld).", 198 error_str ? error_str : "<unknown error>", 199 (long)open_app_error); 200 } 201 202 [system_service release]; 203 dispatch_semaphore_signal(semaphore); 204 } 205 206 ]; 207 208 const uint32_t timeout_secs = 30; 209 210 dispatch_time_t timeout = 211 dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); 212 213 long success = dispatch_semaphore_wait(semaphore, timeout) == 0; 214 215 dispatch_release(semaphore); 216 217 DNBLog("[LaunchAttach] END (%d) templated *Board launcher finished app lunch " 218 "request for " 219 "'%s'", 220 getpid(), cstr); 221 222 if (!success) { 223 DNBLogError("[LaunchAttach] END (%d) timed out trying to send " 224 "openApplication to %s.", 225 getpid(), cstr); 226 error.SetError(OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); 227 error.SetErrorString("timed out trying to launch app"); 228 } else if (open_app_error != no_error_enum_value) { 229 error_function(open_app_error, open_app_error_string, error); 230 DNBLogError("[LaunchAttach] END (%d) unable to launch the application with " 231 "CFBundleIdentifier '%s' " 232 "bks_error = %ld", 233 getpid(), cstr, (long)open_app_error); 234 success = false; 235 } else if (wants_pid) { 236 *return_pid = pid_in_block; 237 DNBLogThreadedIf( 238 LOG_PROCESS, 239 "Out of completion handler, pid from block %d and passing out: %d", 240 pid_in_block, *return_pid); 241 } 242 243 return success; 244} 245#endif 246 247#if defined(WITH_BKS) || defined(WITH_FBS) 248static void SplitEventData(const char *data, std::vector<std::string> &elements) 249{ 250 elements.clear(); 251 if (!data) 252 return; 253 254 const char *start = data; 255 256 while (*start != '\0') { 257 const char *token = strchr(start, ':'); 258 if (!token) { 259 elements.push_back(std::string(start)); 260 return; 261 } 262 if (token != start) 263 elements.push_back(std::string(start, token - start)); 264 start = ++token; 265 } 266} 267#endif 268 269#ifdef WITH_BKS 270#import <Foundation/Foundation.h> 271extern "C" { 272#import <BackBoardServices/BKSOpenApplicationConstants_Private.h> 273#import <BackBoardServices/BKSSystemService_LaunchServices.h> 274#import <BackBoardServices/BackBoardServices.h> 275} 276 277static bool IsBKSProcess(nub_process_t pid) { 278 BKSApplicationStateMonitor *state_monitor = 279 [[BKSApplicationStateMonitor alloc] init]; 280 BKSApplicationState app_state = 281 [state_monitor mostElevatedApplicationStateForPID:pid]; 282 return app_state != BKSApplicationStateUnknown; 283} 284 285static void SetBKSError(NSInteger error_code, 286 std::string error_description, 287 DNBError &error) { 288 error.SetError(error_code, DNBError::BackBoard); 289 NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString( 290 (BKSOpenApplicationErrorCode)error_code); 291 std::string err_str = "unknown BKS error"; 292 if (error_description.empty() == false) { 293 err_str = error_description; 294 } else if (err_nsstr != nullptr) { 295 err_str = [err_nsstr UTF8String]; 296 } 297 error.SetErrorString(err_str.c_str()); 298} 299 300static bool BKSAddEventDataToOptions(NSMutableDictionary *options, 301 const char *event_data, 302 DNBError &option_error) { 303 std::vector<std::string> values; 304 SplitEventData(event_data, values); 305 bool found_one = false; 306 for (std::string value : values) 307 { 308 if (value.compare("BackgroundContentFetching") == 0) { 309 DNBLog("Setting ActivateForEvent key in options dictionary."); 310 NSDictionary *event_details = [NSDictionary dictionary]; 311 NSDictionary *event_dictionary = [NSDictionary 312 dictionaryWithObject:event_details 313 forKey: 314 BKSActivateForEventOptionTypeBackgroundContentFetching]; 315 [options setObject:event_dictionary 316 forKey:BKSOpenApplicationOptionKeyActivateForEvent]; 317 found_one = true; 318 } else if (value.compare("ActivateSuspended") == 0) { 319 DNBLog("Setting ActivateSuspended key in options dictionary."); 320 [options setObject:@YES forKey: BKSOpenApplicationOptionKeyActivateSuspended]; 321 found_one = true; 322 } else { 323 DNBLogError("Unrecognized event type: %s. Ignoring.", value.c_str()); 324 option_error.SetErrorString("Unrecognized event data"); 325 } 326 } 327 return found_one; 328} 329 330static NSMutableDictionary *BKSCreateOptionsDictionary( 331 const char *app_bundle_path, NSMutableArray *launch_argv, 332 NSMutableDictionary *launch_envp, NSString *stdio_path, bool disable_aslr, 333 const char *event_data) { 334 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 335 if (launch_argv != nil) 336 [debug_options setObject:launch_argv forKey:BKSDebugOptionKeyArguments]; 337 if (launch_envp != nil) 338 [debug_options setObject:launch_envp forKey:BKSDebugOptionKeyEnvironment]; 339 340 [debug_options setObject:stdio_path forKey:BKSDebugOptionKeyStandardOutPath]; 341 [debug_options setObject:stdio_path 342 forKey:BKSDebugOptionKeyStandardErrorPath]; 343 [debug_options setObject:[NSNumber numberWithBool:YES] 344 forKey:BKSDebugOptionKeyWaitForDebugger]; 345 if (disable_aslr) 346 [debug_options setObject:[NSNumber numberWithBool:YES] 347 forKey:BKSDebugOptionKeyDisableASLR]; 348 349 // That will go in the overall dictionary: 350 351 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 352 [options setObject:debug_options 353 forKey:BKSOpenApplicationOptionKeyDebuggingOptions]; 354 // And there are some other options at the top level in this dictionary: 355 [options setObject:[NSNumber numberWithBool:YES] 356 forKey:BKSOpenApplicationOptionKeyUnlockDevice]; 357 358 DNBError error; 359 BKSAddEventDataToOptions(options, event_data, error); 360 361 return options; 362} 363 364static CallOpenApplicationFunction BKSCallOpenApplicationFunction = 365 CallBoardSystemServiceOpenApplication< 366 BKSSystemService, BKSOpenApplicationErrorCode, 367 BKSOpenApplicationErrorCodeNone, SetBKSError>; 368#endif // WITH_BKS 369 370#ifdef WITH_FBS 371#import <Foundation/Foundation.h> 372extern "C" { 373#import <FrontBoardServices/FBSOpenApplicationConstants_Private.h> 374#import <FrontBoardServices/FBSSystemService_LaunchServices.h> 375#import <FrontBoardServices/FrontBoardServices.h> 376#import <MobileCoreServices/LSResourceProxy.h> 377#import <MobileCoreServices/MobileCoreServices.h> 378} 379 380#ifdef WITH_BKS 381static bool IsFBSProcess(nub_process_t pid) { 382 BKSApplicationStateMonitor *state_monitor = 383 [[BKSApplicationStateMonitor alloc] init]; 384 BKSApplicationState app_state = 385 [state_monitor mostElevatedApplicationStateForPID:pid]; 386 return app_state != BKSApplicationStateUnknown; 387} 388#else 389static bool IsFBSProcess(nub_process_t pid) { 390 // FIXME: What is the FBS equivalent of BKSApplicationStateMonitor 391 return false; 392} 393#endif 394 395static void SetFBSError(NSInteger error_code, 396 std::string error_description, 397 DNBError &error) { 398 error.SetError((DNBError::ValueType)error_code, DNBError::FrontBoard); 399 NSString *err_nsstr = ::FBSOpenApplicationErrorCodeToString( 400 (FBSOpenApplicationErrorCode)error_code); 401 std::string err_str = "unknown FBS error"; 402 if (error_description.empty() == false) { 403 err_str = error_description; 404 } else if (err_nsstr != nullptr) { 405 err_str = [err_nsstr UTF8String]; 406 } 407 error.SetErrorString(err_str.c_str()); 408} 409 410static bool FBSAddEventDataToOptions(NSMutableDictionary *options, 411 const char *event_data, 412 DNBError &option_error) { 413 std::vector<std::string> values; 414 SplitEventData(event_data, values); 415 bool found_one = false; 416 for (std::string value : values) 417 { 418 if (value.compare("BackgroundContentFetching") == 0) { 419 DNBLog("Setting ActivateForEvent key in options dictionary."); 420 NSDictionary *event_details = [NSDictionary dictionary]; 421 NSDictionary *event_dictionary = [NSDictionary 422 dictionaryWithObject:event_details 423 forKey: 424 FBSActivateForEventOptionTypeBackgroundContentFetching]; 425 [options setObject:event_dictionary 426 forKey:FBSOpenApplicationOptionKeyActivateForEvent]; 427 found_one = true; 428 } else if (value.compare("ActivateSuspended") == 0) { 429 DNBLog("Setting ActivateSuspended key in options dictionary."); 430 [options setObject:@YES forKey: FBSOpenApplicationOptionKeyActivateSuspended]; 431 found_one = true; 432#if WITH_CAROUSEL 433 } else if (value.compare("WatchComplicationLaunch") == 0) { 434 DNBLog("Setting FBSOpenApplicationOptionKeyActivateSuspended key in options dictionary."); 435 [options setObject:@YES forKey: CSLSOpenApplicationOptionForClockKit]; 436 found_one = true; 437#endif // WITH_CAROUSEL 438 } else { 439 DNBLogError("Unrecognized event type: %s. Ignoring.", value.c_str()); 440 option_error.SetErrorString("Unrecognized event data."); 441 } 442 } 443 return found_one; 444} 445 446static NSMutableDictionary * 447FBSCreateOptionsDictionary(const char *app_bundle_path, 448 NSMutableArray *launch_argv, 449 NSDictionary *launch_envp, NSString *stdio_path, 450 bool disable_aslr, const char *event_data) { 451 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 452 453 if (launch_argv != nil) 454 [debug_options setObject:launch_argv forKey:FBSDebugOptionKeyArguments]; 455 if (launch_envp != nil) 456 [debug_options setObject:launch_envp forKey:FBSDebugOptionKeyEnvironment]; 457 458 [debug_options setObject:stdio_path forKey:FBSDebugOptionKeyStandardOutPath]; 459 [debug_options setObject:stdio_path 460 forKey:FBSDebugOptionKeyStandardErrorPath]; 461 [debug_options setObject:[NSNumber numberWithBool:YES] 462 forKey:FBSDebugOptionKeyWaitForDebugger]; 463 if (disable_aslr) 464 [debug_options setObject:[NSNumber numberWithBool:YES] 465 forKey:FBSDebugOptionKeyDisableASLR]; 466 467 // That will go in the overall dictionary: 468 469 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 470 [options setObject:debug_options 471 forKey:FBSOpenApplicationOptionKeyDebuggingOptions]; 472 // And there are some other options at the top level in this dictionary: 473 [options setObject:[NSNumber numberWithBool:YES] 474 forKey:FBSOpenApplicationOptionKeyUnlockDevice]; 475 [options setObject:[NSNumber numberWithBool:YES] 476 forKey:FBSOpenApplicationOptionKeyPromptUnlockDevice]; 477 478 // We have to get the "sequence ID & UUID" for this app bundle path and send 479 // them to FBS: 480 481 NSURL *app_bundle_url = 482 [NSURL fileURLWithPath:[NSString stringWithUTF8String:app_bundle_path] 483 isDirectory:YES]; 484 LSApplicationProxy *app_proxy = 485 [LSApplicationProxy applicationProxyForBundleURL:app_bundle_url]; 486 if (app_proxy) { 487 DNBLog("Sending AppProxy info: sequence no: %lu, GUID: %s.", 488 app_proxy.sequenceNumber, 489 [app_proxy.cacheGUID.UUIDString UTF8String]); 490 [options 491 setObject:[NSNumber numberWithUnsignedInteger:app_proxy.sequenceNumber] 492 forKey:FBSOpenApplicationOptionKeyLSSequenceNumber]; 493 [options setObject:app_proxy.cacheGUID.UUIDString 494 forKey:FBSOpenApplicationOptionKeyLSCacheGUID]; 495 } 496 497 DNBError error; 498 FBSAddEventDataToOptions(options, event_data, error); 499 500 return options; 501} 502static CallOpenApplicationFunction FBSCallOpenApplicationFunction = 503 CallBoardSystemServiceOpenApplication< 504 FBSSystemService, FBSOpenApplicationErrorCode, 505 FBSOpenApplicationErrorCodeNone, SetFBSError>; 506#endif // WITH_FBS 507 508#if 0 509#define DEBUG_LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) 510#else 511#define DEBUG_LOG(fmt, ...) 512#endif 513 514#ifndef MACH_PROCESS_USE_POSIX_SPAWN 515#define MACH_PROCESS_USE_POSIX_SPAWN 1 516#endif 517 518#ifndef _POSIX_SPAWN_DISABLE_ASLR 519#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 520#endif 521 522MachProcess::MachProcess() 523 : m_pid(0), m_cpu_type(0), m_child_stdin(-1), m_child_stdout(-1), 524 m_child_stderr(-1), m_path(), m_args(), m_task(this), 525 m_flags(eMachProcessFlagsNone), m_stdio_thread(0), 526 m_stdio_mutex(PTHREAD_MUTEX_RECURSIVE), m_stdout_data(), 527 m_profile_enabled(false), m_profile_interval_usec(0), m_profile_thread(0), 528 m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE), m_profile_data(), 529 m_profile_events(0, eMachProcessProfileCancel), m_thread_actions(), 530 m_exception_messages(), 531 m_exception_messages_mutex(PTHREAD_MUTEX_RECURSIVE), m_thread_list(), 532 m_activities(), m_state(eStateUnloaded), 533 m_state_mutex(PTHREAD_MUTEX_RECURSIVE), m_events(0, kAllEventsMask), 534 m_private_events(0, kAllEventsMask), m_breakpoints(), m_watchpoints(), 535 m_name_to_addr_callback(NULL), m_name_to_addr_baton(NULL), 536 m_image_infos_callback(NULL), m_image_infos_baton(NULL), 537 m_sent_interrupt_signo(0), m_auto_resume_signo(0), m_did_exec(false), 538 m_dyld_process_info_create(nullptr), 539 m_dyld_process_info_for_each_image(nullptr), 540 m_dyld_process_info_release(nullptr), 541 m_dyld_process_info_get_cache(nullptr), 542 m_dyld_process_info_get_state(nullptr) { 543 m_dyld_process_info_create = 544 (void *(*)(task_t task, uint64_t timestamp, kern_return_t * kernelError)) 545 dlsym(RTLD_DEFAULT, "_dyld_process_info_create"); 546 m_dyld_process_info_for_each_image = 547 (void (*)(void *info, void (^)(uint64_t machHeaderAddress, 548 const uuid_t uuid, const char *path))) 549 dlsym(RTLD_DEFAULT, "_dyld_process_info_for_each_image"); 550 m_dyld_process_info_release = 551 (void (*)(void *info))dlsym(RTLD_DEFAULT, "_dyld_process_info_release"); 552 m_dyld_process_info_get_cache = (void (*)(void *info, void *cacheInfo))dlsym( 553 RTLD_DEFAULT, "_dyld_process_info_get_cache"); 554 m_dyld_process_info_get_platform = (uint32_t (*)(void *info))dlsym( 555 RTLD_DEFAULT, "_dyld_process_info_get_platform"); 556 m_dyld_process_info_get_state = (void (*)(void *info, void *stateInfo))dlsym( 557 RTLD_DEFAULT, "_dyld_process_info_get_state"); 558 559 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); 560} 561 562MachProcess::~MachProcess() { 563 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); 564 Clear(); 565} 566 567pid_t MachProcess::SetProcessID(pid_t pid) { 568 // Free any previous process specific data or resources 569 Clear(); 570 // Set the current PID appropriately 571 if (pid == 0) 572 m_pid = ::getpid(); 573 else 574 m_pid = pid; 575 return m_pid; // Return actually PID in case a zero pid was passed in 576} 577 578nub_state_t MachProcess::GetState() { 579 // If any other threads access this we will need a mutex for it 580 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); 581 return m_state; 582} 583 584const char *MachProcess::ThreadGetName(nub_thread_t tid) { 585 return m_thread_list.GetName(tid); 586} 587 588nub_state_t MachProcess::ThreadGetState(nub_thread_t tid) { 589 return m_thread_list.GetState(tid); 590} 591 592nub_size_t MachProcess::GetNumThreads() const { 593 return m_thread_list.NumThreads(); 594} 595 596nub_thread_t MachProcess::GetThreadAtIndex(nub_size_t thread_idx) const { 597 return m_thread_list.ThreadIDAtIndex(thread_idx); 598} 599 600nub_thread_t 601MachProcess::GetThreadIDForMachPortNumber(thread_t mach_port_number) const { 602 return m_thread_list.GetThreadIDByMachPortNumber(mach_port_number); 603} 604 605nub_bool_t MachProcess::SyncThreadState(nub_thread_t tid) { 606 MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid)); 607 if (!thread_sp) 608 return false; 609 kern_return_t kret = ::thread_abort_safely(thread_sp->MachPortNumber()); 610 DNBLogThreadedIf(LOG_THREAD, "thread = 0x%8.8" PRIx32 611 " calling thread_abort_safely (tid) => %u " 612 "(GetGPRState() for stop_count = %u)", 613 thread_sp->MachPortNumber(), kret, 614 thread_sp->Process()->StopCount()); 615 616 if (kret == KERN_SUCCESS) 617 return true; 618 else 619 return false; 620} 621 622ThreadInfo::QoS MachProcess::GetRequestedQoS(nub_thread_t tid, nub_addr_t tsd, 623 uint64_t dti_qos_class_index) { 624 return m_thread_list.GetRequestedQoS(tid, tsd, dti_qos_class_index); 625} 626 627nub_addr_t MachProcess::GetPThreadT(nub_thread_t tid) { 628 return m_thread_list.GetPThreadT(tid); 629} 630 631nub_addr_t MachProcess::GetDispatchQueueT(nub_thread_t tid) { 632 return m_thread_list.GetDispatchQueueT(tid); 633} 634 635nub_addr_t MachProcess::GetTSDAddressForThread( 636 nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, 637 uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) { 638 return m_thread_list.GetTSDAddressForThread( 639 tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, 640 plo_pthread_tsd_entry_size); 641} 642 643MachProcess::DeploymentInfo 644MachProcess::GetDeploymentInfo(const struct load_command &lc, 645 uint64_t load_command_address, 646 bool is_executable) { 647 DeploymentInfo info; 648 uint32_t cmd = lc.cmd & ~LC_REQ_DYLD; 649 650 // Handle the older LC_VERSION load commands, which don't 651 // distinguish between simulator and real hardware. 652 auto handle_version_min = [&](char platform) { 653 struct version_min_command vers_cmd; 654 if (ReadMemory(load_command_address, sizeof(struct version_min_command), 655 &vers_cmd) != sizeof(struct version_min_command)) 656 return; 657 info.platform = platform; 658 info.major_version = vers_cmd.version >> 16; 659 info.minor_version = (vers_cmd.version >> 8) & 0xffu; 660 info.patch_version = vers_cmd.version & 0xffu; 661 662 // Disambiguate legacy simulator platforms. 663#if (defined(__x86_64__) || defined(__i386__)) 664 // If we are running on Intel macOS, it is safe to assume this is 665 // really a back-deploying simulator binary. 666 switch (info.platform) { 667 case PLATFORM_IOS: 668 info.platform = PLATFORM_IOSSIMULATOR; 669 break; 670 case PLATFORM_TVOS: 671 info.platform = PLATFORM_TVOSSIMULATOR; 672 break; 673 case PLATFORM_WATCHOS: 674 info.platform = PLATFORM_WATCHOSSIMULATOR; 675 break; 676 } 677#else 678 // On an Apple Silicon macOS host, there is no ambiguity. The only 679 // binaries that use legacy load commands are back-deploying 680 // native iOS binaries. All simulator binaries use the newer, 681 // unambiguous LC_BUILD_VERSION load commands. 682#endif 683 }; 684 685 switch (cmd) { 686 case LC_VERSION_MIN_IPHONEOS: 687 handle_version_min(PLATFORM_IOS); 688 break; 689 case LC_VERSION_MIN_MACOSX: 690 handle_version_min(PLATFORM_MACOS); 691 break; 692 case LC_VERSION_MIN_TVOS: 693 handle_version_min(PLATFORM_TVOS); 694 break; 695 case LC_VERSION_MIN_WATCHOS: 696 handle_version_min(PLATFORM_WATCHOS); 697 break; 698#if defined(LC_BUILD_VERSION) 699 case LC_BUILD_VERSION: { 700 struct build_version_command build_vers; 701 if (ReadMemory(load_command_address, sizeof(struct build_version_command), 702 &build_vers) != sizeof(struct build_version_command)) 703 break; 704 info.platform = build_vers.platform; 705 info.major_version = build_vers.minos >> 16; 706 info.minor_version = (build_vers.minos >> 8) & 0xffu; 707 info.patch_version = build_vers.minos & 0xffu; 708 break; 709 } 710#endif 711 } 712 713 // The xctest binary is a pure macOS binary but is launched with 714 // DYLD_FORCE_PLATFORM=6. In that case, force the platform to 715 // macCatalyst and use the macCatalyst version of the host OS 716 // instead of the macOS deployment target. 717 if (is_executable && GetPlatform() == PLATFORM_MACCATALYST) { 718 info.platform = PLATFORM_MACCATALYST; 719 std::string catalyst_version = GetMacCatalystVersionString(); 720 const char *major = catalyst_version.c_str(); 721 char *minor = nullptr; 722 char *patch = nullptr; 723 info.major_version = std::strtoul(major, &minor, 10); 724 info.minor_version = 0; 725 info.patch_version = 0; 726 if (minor && *minor == '.') { 727 info.minor_version = std::strtoul(++minor, &patch, 10); 728 if (patch && *patch == '.') 729 info.patch_version = std::strtoul(++patch, nullptr, 10); 730 } 731 } 732 733 return info; 734} 735 736std::optional<std::string> 737MachProcess::GetPlatformString(unsigned char platform) { 738 switch (platform) { 739 case PLATFORM_MACOS: 740 return "macosx"; 741 case PLATFORM_MACCATALYST: 742 return "maccatalyst"; 743 case PLATFORM_IOS: 744 return "ios"; 745 case PLATFORM_IOSSIMULATOR: 746 return "iossimulator"; 747 case PLATFORM_TVOS: 748 return "tvos"; 749 case PLATFORM_TVOSSIMULATOR: 750 return "tvossimulator"; 751 case PLATFORM_WATCHOS: 752 return "watchos"; 753 case PLATFORM_WATCHOSSIMULATOR: 754 return "watchossimulator"; 755 case PLATFORM_BRIDGEOS: 756 return "bridgeos"; 757 case PLATFORM_DRIVERKIT: 758 return "driverkit"; 759 case PLATFORM_VISIONOS: 760 return "xros"; 761 case PLATFORM_VISIONOSSIMULATOR: 762 return "xrossimulator"; 763 default: 764 DNBLogError("Unknown platform %u found for one binary", platform); 765 return std::nullopt; 766 } 767} 768 769static bool mach_header_validity_test(uint32_t magic, uint32_t cputype) { 770 if (magic != MH_MAGIC && magic != MH_CIGAM && magic != MH_MAGIC_64 && 771 magic != MH_CIGAM_64) 772 return false; 773 if (cputype != CPU_TYPE_I386 && cputype != CPU_TYPE_X86_64 && 774 cputype != CPU_TYPE_ARM && cputype != CPU_TYPE_ARM64 && 775 cputype != CPU_TYPE_ARM64_32) 776 return false; 777 return true; 778} 779 780// Given an address, read the mach-o header and load commands out of memory to 781// fill in 782// the mach_o_information "inf" object. 783// 784// Returns false if there was an error in reading this mach-o file header/load 785// commands. 786 787bool MachProcess::GetMachOInformationFromMemory( 788 uint32_t dyld_platform, nub_addr_t mach_o_header_addr, int wordsize, 789 struct mach_o_information &inf) { 790 uint64_t load_cmds_p; 791 792 if (wordsize == 4) { 793 struct mach_header header; 794 if (ReadMemory(mach_o_header_addr, sizeof(struct mach_header), &header) != 795 sizeof(struct mach_header)) { 796 return false; 797 } 798 if (!mach_header_validity_test(header.magic, header.cputype)) 799 return false; 800 801 load_cmds_p = mach_o_header_addr + sizeof(struct mach_header); 802 inf.mach_header.magic = header.magic; 803 inf.mach_header.cputype = header.cputype; 804 // high byte of cpusubtype is used for "capability bits", v. 805 // CPU_SUBTYPE_MASK, CPU_SUBTYPE_LIB64 in machine.h 806 inf.mach_header.cpusubtype = header.cpusubtype & 0x00ffffff; 807 inf.mach_header.filetype = header.filetype; 808 inf.mach_header.ncmds = header.ncmds; 809 inf.mach_header.sizeofcmds = header.sizeofcmds; 810 inf.mach_header.flags = header.flags; 811 } else { 812 struct mach_header_64 header; 813 if (ReadMemory(mach_o_header_addr, sizeof(struct mach_header_64), 814 &header) != sizeof(struct mach_header_64)) { 815 return false; 816 } 817 if (!mach_header_validity_test(header.magic, header.cputype)) 818 return false; 819 load_cmds_p = mach_o_header_addr + sizeof(struct mach_header_64); 820 inf.mach_header.magic = header.magic; 821 inf.mach_header.cputype = header.cputype; 822 // high byte of cpusubtype is used for "capability bits", v. 823 // CPU_SUBTYPE_MASK, CPU_SUBTYPE_LIB64 in machine.h 824 inf.mach_header.cpusubtype = header.cpusubtype & 0x00ffffff; 825 inf.mach_header.filetype = header.filetype; 826 inf.mach_header.ncmds = header.ncmds; 827 inf.mach_header.sizeofcmds = header.sizeofcmds; 828 inf.mach_header.flags = header.flags; 829 } 830 for (uint32_t j = 0; j < inf.mach_header.ncmds; j++) { 831 struct load_command lc; 832 if (ReadMemory(load_cmds_p, sizeof(struct load_command), &lc) != 833 sizeof(struct load_command)) { 834 return false; 835 } 836 if (lc.cmd == LC_SEGMENT) { 837 struct segment_command seg; 838 if (ReadMemory(load_cmds_p, sizeof(struct segment_command), &seg) != 839 sizeof(struct segment_command)) { 840 return false; 841 } 842 struct mach_o_segment this_seg; 843 char name[17]; 844 ::memset(name, 0, sizeof(name)); 845 memcpy(name, seg.segname, sizeof(seg.segname)); 846 this_seg.name = name; 847 this_seg.vmaddr = seg.vmaddr; 848 this_seg.vmsize = seg.vmsize; 849 this_seg.fileoff = seg.fileoff; 850 this_seg.filesize = seg.filesize; 851 this_seg.maxprot = seg.maxprot; 852 this_seg.initprot = seg.initprot; 853 this_seg.nsects = seg.nsects; 854 this_seg.flags = seg.flags; 855 inf.segments.push_back(this_seg); 856 if (this_seg.name == "ExecExtraSuspend") 857 m_task.TaskWillExecProcessesSuspended(); 858 } 859 if (lc.cmd == LC_SEGMENT_64) { 860 struct segment_command_64 seg; 861 if (ReadMemory(load_cmds_p, sizeof(struct segment_command_64), &seg) != 862 sizeof(struct segment_command_64)) { 863 return false; 864 } 865 struct mach_o_segment this_seg; 866 char name[17]; 867 ::memset(name, 0, sizeof(name)); 868 memcpy(name, seg.segname, sizeof(seg.segname)); 869 this_seg.name = name; 870 this_seg.vmaddr = seg.vmaddr; 871 this_seg.vmsize = seg.vmsize; 872 this_seg.fileoff = seg.fileoff; 873 this_seg.filesize = seg.filesize; 874 this_seg.maxprot = seg.maxprot; 875 this_seg.initprot = seg.initprot; 876 this_seg.nsects = seg.nsects; 877 this_seg.flags = seg.flags; 878 inf.segments.push_back(this_seg); 879 if (this_seg.name == "ExecExtraSuspend") 880 m_task.TaskWillExecProcessesSuspended(); 881 } 882 if (lc.cmd == LC_UUID) { 883 struct uuid_command uuidcmd; 884 if (ReadMemory(load_cmds_p, sizeof(struct uuid_command), &uuidcmd) == 885 sizeof(struct uuid_command)) 886 uuid_copy(inf.uuid, uuidcmd.uuid); 887 } 888 if (DeploymentInfo deployment_info = GetDeploymentInfo( 889 lc, load_cmds_p, inf.mach_header.filetype == MH_EXECUTE)) { 890 std::optional<std::string> lc_platform = 891 GetPlatformString(deployment_info.platform); 892 if (dyld_platform != PLATFORM_MACCATALYST && 893 inf.min_version_os_name == "macosx") { 894 // macCatalyst support. 895 // 896 // This the special case of "zippered" frameworks that have both 897 // a PLATFORM_MACOS and a PLATFORM_MACCATALYST load command. 898 // 899 // When we are in this block, this is a binary with both 900 // PLATFORM_MACOS and PLATFORM_MACCATALYST load commands and 901 // the process is not running as PLATFORM_MACCATALYST. Stick 902 // with the "macosx" load command that we've already 903 // processed, ignore this one, which is presumed to be a 904 // PLATFORM_MACCATALYST one. 905 } else { 906 inf.min_version_os_name = lc_platform.value_or(""); 907 inf.min_version_os_version = ""; 908 inf.min_version_os_version += 909 std::to_string(deployment_info.major_version); 910 inf.min_version_os_version += "."; 911 inf.min_version_os_version += 912 std::to_string(deployment_info.minor_version); 913 if (deployment_info.patch_version != 0) { 914 inf.min_version_os_version += "."; 915 inf.min_version_os_version += 916 std::to_string(deployment_info.patch_version); 917 } 918 } 919 } 920 921 load_cmds_p += lc.cmdsize; 922 } 923 return true; 924} 925 926// Given completely filled in array of binary_image_information structures, 927// create a JSONGenerator object 928// with all the details we want to send to lldb. 929JSONGenerator::ObjectSP MachProcess::FormatDynamicLibrariesIntoJSON( 930 const std::vector<struct binary_image_information> &image_infos, 931 bool report_load_commands) { 932 933 JSONGenerator::ArraySP image_infos_array_sp(new JSONGenerator::Array()); 934 935 const size_t image_count = image_infos.size(); 936 937 for (size_t i = 0; i < image_count; i++) { 938 // If we should report the Mach-O header and load commands, 939 // and those were unreadable, don't report anything about this 940 // binary. 941 if (report_load_commands && !image_infos[i].is_valid_mach_header) 942 continue; 943 JSONGenerator::DictionarySP image_info_dict_sp( 944 new JSONGenerator::Dictionary()); 945 image_info_dict_sp->AddIntegerItem("load_address", 946 image_infos[i].load_address); 947 // TODO: lldb currently rejects a response without this, but it 948 // is always zero from dyld. It can be removed once we've had time 949 // for lldb's that require it to be present are obsolete. 950 image_info_dict_sp->AddIntegerItem("mod_date", 0); 951 image_info_dict_sp->AddStringItem("pathname", image_infos[i].filename); 952 953 if (!report_load_commands) { 954 image_infos_array_sp->AddItem(image_info_dict_sp); 955 continue; 956 } 957 958 uuid_string_t uuidstr; 959 uuid_unparse_upper(image_infos[i].macho_info.uuid, uuidstr); 960 image_info_dict_sp->AddStringItem("uuid", uuidstr); 961 962 if (!image_infos[i].macho_info.min_version_os_name.empty() && 963 !image_infos[i].macho_info.min_version_os_version.empty()) { 964 image_info_dict_sp->AddStringItem( 965 "min_version_os_name", image_infos[i].macho_info.min_version_os_name); 966 image_info_dict_sp->AddStringItem( 967 "min_version_os_sdk", 968 image_infos[i].macho_info.min_version_os_version); 969 } 970 971 JSONGenerator::DictionarySP mach_header_dict_sp( 972 new JSONGenerator::Dictionary()); 973 mach_header_dict_sp->AddIntegerItem( 974 "magic", image_infos[i].macho_info.mach_header.magic); 975 mach_header_dict_sp->AddIntegerItem( 976 "cputype", (uint32_t)image_infos[i].macho_info.mach_header.cputype); 977 mach_header_dict_sp->AddIntegerItem( 978 "cpusubtype", 979 (uint32_t)image_infos[i].macho_info.mach_header.cpusubtype); 980 mach_header_dict_sp->AddIntegerItem( 981 "filetype", image_infos[i].macho_info.mach_header.filetype); 982 mach_header_dict_sp->AddIntegerItem ("flags", 983 image_infos[i].macho_info.mach_header.flags); 984 985 // DynamicLoaderMacOSX doesn't currently need these fields, so 986 // don't send them. 987 // mach_header_dict_sp->AddIntegerItem ("ncmds", 988 // image_infos[i].macho_info.mach_header.ncmds); 989 // mach_header_dict_sp->AddIntegerItem ("sizeofcmds", 990 // image_infos[i].macho_info.mach_header.sizeofcmds); 991 image_info_dict_sp->AddItem("mach_header", mach_header_dict_sp); 992 993 JSONGenerator::ArraySP segments_sp(new JSONGenerator::Array()); 994 for (size_t j = 0; j < image_infos[i].macho_info.segments.size(); j++) { 995 JSONGenerator::DictionarySP segment_sp(new JSONGenerator::Dictionary()); 996 segment_sp->AddStringItem("name", 997 image_infos[i].macho_info.segments[j].name); 998 segment_sp->AddIntegerItem("vmaddr", 999 image_infos[i].macho_info.segments[j].vmaddr); 1000 segment_sp->AddIntegerItem("vmsize", 1001 image_infos[i].macho_info.segments[j].vmsize); 1002 segment_sp->AddIntegerItem("fileoff", 1003 image_infos[i].macho_info.segments[j].fileoff); 1004 segment_sp->AddIntegerItem( 1005 "filesize", image_infos[i].macho_info.segments[j].filesize); 1006 segment_sp->AddIntegerItem("maxprot", 1007 image_infos[i].macho_info.segments[j].maxprot); 1008 1009 // DynamicLoaderMacOSX doesn't currently need these fields, 1010 // so don't send them. 1011 // segment_sp->AddIntegerItem ("initprot", 1012 // image_infos[i].macho_info.segments[j].initprot); 1013 // segment_sp->AddIntegerItem ("nsects", 1014 // image_infos[i].macho_info.segments[j].nsects); 1015 // segment_sp->AddIntegerItem ("flags", 1016 // image_infos[i].macho_info.segments[j].flags); 1017 segments_sp->AddItem(segment_sp); 1018 } 1019 image_info_dict_sp->AddItem("segments", segments_sp); 1020 1021 image_infos_array_sp->AddItem(image_info_dict_sp); 1022 } 1023 1024 JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary()); 1025 reply_sp->AddItem("images", image_infos_array_sp); 1026 1027 return reply_sp; 1028} 1029 1030/// From dyld SPI header dyld_process_info.h 1031typedef void *dyld_process_info; 1032struct dyld_process_cache_info { 1033 /// UUID of cache used by process. 1034 uuid_t cacheUUID; 1035 /// Load address of dyld shared cache. 1036 uint64_t cacheBaseAddress; 1037 /// Process is running without a dyld cache. 1038 bool noCache; 1039 /// Process is using a private copy of its dyld cache. 1040 bool privateCache; 1041}; 1042 1043uint32_t MachProcess::GetPlatform() { 1044 if (m_platform == 0) 1045 m_platform = MachProcess::GetProcessPlatformViaDYLDSPI(); 1046 return m_platform; 1047} 1048 1049uint32_t MachProcess::GetProcessPlatformViaDYLDSPI() { 1050 kern_return_t kern_ret; 1051 uint32_t platform = 0; 1052 if (m_dyld_process_info_create) { 1053 dyld_process_info info = 1054 m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret); 1055 if (info) { 1056 if (m_dyld_process_info_get_platform) 1057 platform = m_dyld_process_info_get_platform(info); 1058 m_dyld_process_info_release(info); 1059 } 1060 } 1061 return platform; 1062} 1063 1064void MachProcess::GetAllLoadedBinariesViaDYLDSPI( 1065 std::vector<struct binary_image_information> &image_infos) { 1066 kern_return_t kern_ret; 1067 if (m_dyld_process_info_create) { 1068 dyld_process_info info = 1069 m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret); 1070 if (info) { 1071 // There's a bug in the interaction between dyld and older dyld_sim's 1072 // (e.g. from the iOS 15 simulator) that causes dyld to report the same 1073 // binary twice. We use this set to eliminate the duplicates. 1074 __block std::unordered_set<uint64_t> seen_header_addrs; 1075 m_dyld_process_info_for_each_image( 1076 info, 1077 ^(uint64_t mach_header_addr, const uuid_t uuid, const char *path) { 1078 auto res_pair = seen_header_addrs.insert(mach_header_addr); 1079 if (!res_pair.second) 1080 return; 1081 struct binary_image_information image; 1082 image.filename = path; 1083 uuid_copy(image.macho_info.uuid, uuid); 1084 image.load_address = mach_header_addr; 1085 image_infos.push_back(image); 1086 }); 1087 m_dyld_process_info_release(info); 1088 } 1089 } 1090} 1091 1092// Fetch information about all shared libraries using the dyld SPIs that exist 1093// in 1094// macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer. 1095JSONGenerator::ObjectSP 1096MachProcess::GetAllLoadedLibrariesInfos(nub_process_t pid, 1097 bool report_load_commands) { 1098 1099 int pointer_size = GetInferiorAddrSize(pid); 1100 std::vector<struct binary_image_information> image_infos; 1101 GetAllLoadedBinariesViaDYLDSPI(image_infos); 1102 if (report_load_commands) { 1103 uint32_t platform = GetPlatform(); 1104 const size_t image_count = image_infos.size(); 1105 for (size_t i = 0; i < image_count; i++) { 1106 if (GetMachOInformationFromMemory(platform, image_infos[i].load_address, 1107 pointer_size, 1108 image_infos[i].macho_info)) { 1109 image_infos[i].is_valid_mach_header = true; 1110 } 1111 } 1112 } 1113 return FormatDynamicLibrariesIntoJSON(image_infos, report_load_commands); 1114} 1115 1116std::optional<std::pair<cpu_type_t, cpu_subtype_t>> 1117MachProcess::GetMainBinaryCPUTypes(nub_process_t pid) { 1118 int pointer_size = GetInferiorAddrSize(pid); 1119 std::vector<struct binary_image_information> image_infos; 1120 GetAllLoadedBinariesViaDYLDSPI(image_infos); 1121 uint32_t platform = GetPlatform(); 1122 for (auto &image_info : image_infos) 1123 if (GetMachOInformationFromMemory(platform, image_info.load_address, 1124 pointer_size, image_info.macho_info)) 1125 if (image_info.macho_info.mach_header.filetype == MH_EXECUTE) 1126 return { 1127 {static_cast<cpu_type_t>(image_info.macho_info.mach_header.cputype), 1128 static_cast<cpu_subtype_t>( 1129 image_info.macho_info.mach_header.cpusubtype)}}; 1130 return {}; 1131} 1132 1133// Fetch information about the shared libraries at the given load addresses 1134// using the 1135// dyld SPIs that exist in macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer. 1136JSONGenerator::ObjectSP MachProcess::GetLibrariesInfoForAddresses( 1137 nub_process_t pid, std::vector<uint64_t> &macho_addresses) { 1138 1139 int pointer_size = GetInferiorAddrSize(pid); 1140 1141 // Collect the list of all binaries that dyld knows about in 1142 // the inferior process. 1143 std::vector<struct binary_image_information> all_image_infos; 1144 GetAllLoadedBinariesViaDYLDSPI(all_image_infos); 1145 uint32_t platform = GetPlatform(); 1146 1147 std::vector<struct binary_image_information> image_infos; 1148 const size_t macho_addresses_count = macho_addresses.size(); 1149 const size_t all_image_infos_count = all_image_infos.size(); 1150 1151 for (size_t i = 0; i < macho_addresses_count; i++) { 1152 bool found_matching_entry = false; 1153 for (size_t j = 0; j < all_image_infos_count; j++) { 1154 if (all_image_infos[j].load_address == macho_addresses[i]) { 1155 image_infos.push_back(all_image_infos[j]); 1156 found_matching_entry = true; 1157 } 1158 } 1159 if (!found_matching_entry) { 1160 // dyld doesn't think there is a binary at this address, 1161 // but maybe there isn't a binary YET - let's look in memory 1162 // for a proper mach-o header etc and return what we can. 1163 // We will have an empty filename for the binary (because dyld 1164 // doesn't know about it yet) but we can read all of the mach-o 1165 // load commands from memory directly. 1166 struct binary_image_information entry; 1167 entry.load_address = macho_addresses[i]; 1168 image_infos.push_back(entry); 1169 } 1170 } 1171 1172 const size_t image_infos_count = image_infos.size(); 1173 for (size_t i = 0; i < image_infos_count; i++) { 1174 if (GetMachOInformationFromMemory(platform, image_infos[i].load_address, 1175 pointer_size, 1176 image_infos[i].macho_info)) { 1177 image_infos[i].is_valid_mach_header = true; 1178 } 1179 } 1180 return FormatDynamicLibrariesIntoJSON(image_infos, 1181 /* report_load_commands = */ true); 1182} 1183 1184// From dyld's internal podyld_process_info.h: 1185 1186JSONGenerator::ObjectSP MachProcess::GetSharedCacheInfo(nub_process_t pid) { 1187 JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary()); 1188 1189 kern_return_t kern_ret; 1190 if (m_dyld_process_info_create && m_dyld_process_info_get_cache) { 1191 dyld_process_info info = 1192 m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret); 1193 if (info) { 1194 struct dyld_process_cache_info shared_cache_info; 1195 m_dyld_process_info_get_cache(info, &shared_cache_info); 1196 1197 reply_sp->AddIntegerItem("shared_cache_base_address", 1198 shared_cache_info.cacheBaseAddress); 1199 1200 uuid_string_t uuidstr; 1201 uuid_unparse_upper(shared_cache_info.cacheUUID, uuidstr); 1202 reply_sp->AddStringItem("shared_cache_uuid", uuidstr); 1203 1204 reply_sp->AddBooleanItem("no_shared_cache", shared_cache_info.noCache); 1205 reply_sp->AddBooleanItem("shared_cache_private_cache", 1206 shared_cache_info.privateCache); 1207 1208 m_dyld_process_info_release(info); 1209 } 1210 } 1211 return reply_sp; 1212} 1213 1214nub_thread_t MachProcess::GetCurrentThread() { 1215 return m_thread_list.CurrentThreadID(); 1216} 1217 1218nub_thread_t MachProcess::GetCurrentThreadMachPort() { 1219 return m_thread_list.GetMachPortNumberByThreadID( 1220 m_thread_list.CurrentThreadID()); 1221} 1222 1223nub_thread_t MachProcess::SetCurrentThread(nub_thread_t tid) { 1224 return m_thread_list.SetCurrentThread(tid); 1225} 1226 1227bool MachProcess::GetThreadStoppedReason(nub_thread_t tid, 1228 struct DNBThreadStopInfo *stop_info) { 1229 if (m_thread_list.GetThreadStoppedReason(tid, stop_info)) { 1230 if (m_did_exec) 1231 stop_info->reason = eStopTypeExec; 1232 if (stop_info->reason == eStopTypeWatchpoint) 1233 RefineWatchpointStopInfo(tid, stop_info); 1234 return true; 1235 } 1236 return false; 1237} 1238 1239void MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const { 1240 return m_thread_list.DumpThreadStoppedReason(tid); 1241} 1242 1243const char *MachProcess::GetThreadInfo(nub_thread_t tid) const { 1244 return m_thread_list.GetThreadInfo(tid); 1245} 1246 1247uint32_t MachProcess::GetCPUType() { 1248 if (m_cpu_type == 0 && m_pid != 0) 1249 m_cpu_type = MachProcess::GetCPUTypeForLocalProcess(m_pid); 1250 return m_cpu_type; 1251} 1252 1253const DNBRegisterSetInfo * 1254MachProcess::GetRegisterSetInfo(nub_thread_t tid, 1255 nub_size_t *num_reg_sets) const { 1256 MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid)); 1257 if (thread_sp) { 1258 DNBArchProtocol *arch = thread_sp->GetArchProtocol(); 1259 if (arch) 1260 return arch->GetRegisterSetInfo(num_reg_sets); 1261 } 1262 *num_reg_sets = 0; 1263 return NULL; 1264} 1265 1266bool MachProcess::GetRegisterValue(nub_thread_t tid, uint32_t set, uint32_t reg, 1267 DNBRegisterValue *value) const { 1268 return m_thread_list.GetRegisterValue(tid, set, reg, value); 1269} 1270 1271bool MachProcess::SetRegisterValue(nub_thread_t tid, uint32_t set, uint32_t reg, 1272 const DNBRegisterValue *value) const { 1273 return m_thread_list.SetRegisterValue(tid, set, reg, value); 1274} 1275 1276void MachProcess::SetState(nub_state_t new_state) { 1277 // If any other threads access this we will need a mutex for it 1278 uint32_t event_mask = 0; 1279 1280 // Scope for mutex locker 1281 { 1282 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); 1283 const nub_state_t old_state = m_state; 1284 1285 if (old_state == eStateExited) { 1286 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring new " 1287 "state since current state is exited", 1288 DNBStateAsString(new_state)); 1289 } else if (old_state == new_state) { 1290 DNBLogThreadedIf( 1291 LOG_PROCESS, 1292 "MachProcess::SetState(%s) ignoring redundant state change...", 1293 DNBStateAsString(new_state)); 1294 } else { 1295 if (NUB_STATE_IS_STOPPED(new_state)) 1296 event_mask = eEventProcessStoppedStateChanged; 1297 else 1298 event_mask = eEventProcessRunningStateChanged; 1299 1300 DNBLogThreadedIf( 1301 LOG_PROCESS, "MachProcess::SetState(%s) upating state (previous " 1302 "state was %s), event_mask = 0x%8.8x", 1303 DNBStateAsString(new_state), DNBStateAsString(old_state), event_mask); 1304 1305 m_state = new_state; 1306 if (new_state == eStateStopped) 1307 m_stop_count++; 1308 } 1309 } 1310 1311 if (event_mask != 0) { 1312 m_events.SetEvents(event_mask); 1313 m_private_events.SetEvents(event_mask); 1314 if (event_mask == eEventProcessStoppedStateChanged) 1315 m_private_events.ResetEvents(eEventProcessRunningStateChanged); 1316 else 1317 m_private_events.ResetEvents(eEventProcessStoppedStateChanged); 1318 1319 // Wait for the event bit to reset if a reset ACK is requested 1320 m_events.WaitForResetAck(event_mask); 1321 } 1322} 1323 1324void MachProcess::Clear(bool detaching) { 1325 // Clear any cached thread list while the pid and task are still valid 1326 1327 m_task.Clear(); 1328 m_platform = 0; 1329 // Now clear out all member variables 1330 m_pid = INVALID_NUB_PROCESS; 1331 if (!detaching) 1332 CloseChildFileDescriptors(); 1333 1334 m_path.clear(); 1335 m_args.clear(); 1336 SetState(eStateUnloaded); 1337 m_flags = eMachProcessFlagsNone; 1338 m_stop_count = 0; 1339 m_thread_list.Clear(); 1340 { 1341 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 1342 m_exception_messages.clear(); 1343 } 1344 m_activities.Clear(); 1345 StopProfileThread(); 1346} 1347 1348bool MachProcess::StartSTDIOThread() { 1349 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); 1350 // Create the thread that watches for the child STDIO 1351 return ::pthread_create(&m_stdio_thread, NULL, MachProcess::STDIOThread, 1352 this) == 0; 1353} 1354 1355void MachProcess::SetEnableAsyncProfiling(bool enable, uint64_t interval_usec, 1356 DNBProfileDataScanType scan_type) { 1357 m_profile_enabled = enable; 1358 m_profile_interval_usec = static_cast<useconds_t>(interval_usec); 1359 m_profile_scan_type = scan_type; 1360 1361 if (m_profile_enabled && (m_profile_thread == NULL)) { 1362 StartProfileThread(); 1363 } else if (!m_profile_enabled && m_profile_thread) { 1364 StopProfileThread(); 1365 } 1366} 1367 1368void MachProcess::StopProfileThread() { 1369 if (m_profile_thread == NULL) 1370 return; 1371 m_profile_events.SetEvents(eMachProcessProfileCancel); 1372 pthread_join(m_profile_thread, NULL); 1373 m_profile_thread = NULL; 1374 m_profile_events.ResetEvents(eMachProcessProfileCancel); 1375} 1376 1377/// return 1 if bit position \a bit is set in \a value 1378static uint32_t bit(uint32_t value, uint32_t bit) { 1379 return (value >> bit) & 1u; 1380} 1381 1382// return the bitfield "value[msbit:lsbit]". 1383static uint64_t bits(uint64_t value, uint32_t msbit, uint32_t lsbit) { 1384 assert(msbit >= lsbit); 1385 uint64_t shift_left = sizeof(value) * 8 - 1 - msbit; 1386 value <<= 1387 shift_left; // shift anything above the msbit off of the unsigned edge 1388 value >>= shift_left + lsbit; // shift it back again down to the lsbit 1389 // (including undoing any shift from above) 1390 return value; // return our result 1391} 1392 1393void MachProcess::RefineWatchpointStopInfo( 1394 nub_thread_t tid, struct DNBThreadStopInfo *stop_info) { 1395 const DNBBreakpoint *wp = m_watchpoints.FindNearestWatchpoint( 1396 stop_info->details.watchpoint.mach_exception_addr); 1397 if (wp) { 1398 stop_info->details.watchpoint.addr = wp->Address(); 1399 stop_info->details.watchpoint.hw_idx = wp->GetHardwareIndex(); 1400 DNBLogThreadedIf(LOG_WATCHPOINTS, 1401 "MachProcess::RefineWatchpointStopInfo " 1402 "mach exception addr 0x%llx moved in to nearest " 1403 "watchpoint, 0x%llx-0x%llx", 1404 stop_info->details.watchpoint.mach_exception_addr, 1405 wp->Address(), wp->Address() + wp->ByteSize() - 1); 1406 } else { 1407 stop_info->details.watchpoint.addr = 1408 stop_info->details.watchpoint.mach_exception_addr; 1409 } 1410 1411 stop_info->details.watchpoint.esr_fields_set = false; 1412 std::optional<uint64_t> esr, far; 1413 nub_size_t num_reg_sets = 0; 1414 const DNBRegisterSetInfo *reg_sets = GetRegisterSetInfo(tid, &num_reg_sets); 1415 for (nub_size_t set = 0; set < num_reg_sets; set++) { 1416 if (reg_sets[set].registers == NULL) 1417 continue; 1418 for (uint32_t reg = 0; reg < reg_sets[set].num_registers; ++reg) { 1419 if (strcmp(reg_sets[set].registers[reg].name, "esr") == 0) { 1420 std::unique_ptr<DNBRegisterValue> reg_value = 1421 std::make_unique<DNBRegisterValue>(); 1422 if (GetRegisterValue(tid, set, reg, reg_value.get())) { 1423 esr = reg_value->value.uint64; 1424 } 1425 } 1426 if (strcmp(reg_sets[set].registers[reg].name, "far") == 0) { 1427 std::unique_ptr<DNBRegisterValue> reg_value = 1428 std::make_unique<DNBRegisterValue>(); 1429 if (GetRegisterValue(tid, set, reg, reg_value.get())) { 1430 far = reg_value->value.uint64; 1431 } 1432 } 1433 } 1434 } 1435 1436 if (esr && far) { 1437 if (*far != stop_info->details.watchpoint.mach_exception_addr) { 1438 // AFAIK the kernel is going to put the FAR value in the mach 1439 // exception, if they don't match, it's interesting enough to log it. 1440 DNBLogThreadedIf(LOG_WATCHPOINTS, 1441 "MachProcess::RefineWatchpointStopInfo mach exception " 1442 "addr 0x%llx but FAR register has value 0x%llx", 1443 stop_info->details.watchpoint.mach_exception_addr, *far); 1444 } 1445 uint32_t exception_class = bits(*esr, 31, 26); 1446 1447 // "Watchpoint exception from a lower Exception level" 1448 if (exception_class == 0b110100) { 1449 stop_info->details.watchpoint.esr_fields_set = true; 1450 // Documented in the ARM ARM A-Profile Dec 2022 edition 1451 // Section D17.2 ("General system control registers"), 1452 // Section D17.2.37 "ESR_EL1, Exception Syndrome Register (EL1)", 1453 // "Field Descriptions" 1454 // "ISS encoding for an exception from a Watchpoint exception" 1455 uint32_t iss = bits(*esr, 23, 0); 1456 stop_info->details.watchpoint.esr_fields.iss = iss; 1457 stop_info->details.watchpoint.esr_fields.wpt = 1458 bits(iss, 23, 18); // Watchpoint number 1459 stop_info->details.watchpoint.esr_fields.wptv = 1460 bit(iss, 17); // Watchpoint number Valid 1461 stop_info->details.watchpoint.esr_fields.wpf = 1462 bit(iss, 16); // Watchpoint might be false-positive 1463 stop_info->details.watchpoint.esr_fields.fnp = 1464 bit(iss, 15); // FAR not Precise 1465 stop_info->details.watchpoint.esr_fields.vncr = 1466 bit(iss, 13); // watchpoint from use of VNCR_EL2 reg by EL1 1467 stop_info->details.watchpoint.esr_fields.fnv = 1468 bit(iss, 10); // FAR not Valid 1469 stop_info->details.watchpoint.esr_fields.cm = 1470 bit(iss, 6); // Cache maintenance 1471 stop_info->details.watchpoint.esr_fields.wnr = 1472 bit(iss, 6); // Write not Read 1473 stop_info->details.watchpoint.esr_fields.dfsc = 1474 bits(iss, 5, 0); // Data Fault Status Code 1475 1476 DNBLogThreadedIf(LOG_WATCHPOINTS, 1477 "ESR watchpoint fields parsed: " 1478 "iss = 0x%x, wpt = %u, wptv = %d, wpf = %d, fnp = %d, " 1479 "vncr = %d, fnv = %d, cm = %d, wnr = %d, dfsc = 0x%x", 1480 stop_info->details.watchpoint.esr_fields.iss, 1481 stop_info->details.watchpoint.esr_fields.wpt, 1482 stop_info->details.watchpoint.esr_fields.wptv, 1483 stop_info->details.watchpoint.esr_fields.wpf, 1484 stop_info->details.watchpoint.esr_fields.fnp, 1485 stop_info->details.watchpoint.esr_fields.vncr, 1486 stop_info->details.watchpoint.esr_fields.fnv, 1487 stop_info->details.watchpoint.esr_fields.cm, 1488 stop_info->details.watchpoint.esr_fields.wnr, 1489 stop_info->details.watchpoint.esr_fields.dfsc); 1490 1491 if (stop_info->details.watchpoint.esr_fields.wptv) { 1492 DNBLogThreadedIf(LOG_WATCHPOINTS, 1493 "Watchpoint Valid field true, " 1494 "finding startaddr of watchpoint %d", 1495 stop_info->details.watchpoint.esr_fields.wpt); 1496 stop_info->details.watchpoint.hw_idx = 1497 stop_info->details.watchpoint.esr_fields.wpt; 1498 const DNBBreakpoint *wp = m_watchpoints.FindByHardwareIndex( 1499 stop_info->details.watchpoint.esr_fields.wpt); 1500 if (wp) { 1501 stop_info->details.watchpoint.addr = wp->Address(); 1502 } 1503 } 1504 } 1505 } 1506} 1507 1508bool MachProcess::StartProfileThread() { 1509 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); 1510 // Create the thread that profiles the inferior and reports back if enabled 1511 return ::pthread_create(&m_profile_thread, NULL, MachProcess::ProfileThread, 1512 this) == 0; 1513} 1514 1515nub_addr_t MachProcess::LookupSymbol(const char *name, const char *shlib) { 1516 if (m_name_to_addr_callback != NULL && name && name[0]) 1517 return m_name_to_addr_callback(ProcessID(), name, shlib, 1518 m_name_to_addr_baton); 1519 return INVALID_NUB_ADDRESS; 1520} 1521 1522bool MachProcess::Resume(const DNBThreadResumeActions &thread_actions) { 1523 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()"); 1524 nub_state_t state = GetState(); 1525 1526 if (CanResume(state)) { 1527 m_thread_actions = thread_actions; 1528 PrivateResume(); 1529 return true; 1530 } else if (state == eStateRunning) { 1531 DNBLog("Resume() - task 0x%x is already running, ignoring...", 1532 m_task.TaskPort()); 1533 return true; 1534 } 1535 DNBLog("Resume() - task 0x%x has state %s, can't continue...", 1536 m_task.TaskPort(), DNBStateAsString(state)); 1537 return false; 1538} 1539 1540bool MachProcess::Kill(const struct timespec *timeout_abstime) { 1541 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()"); 1542 nub_state_t state = DoSIGSTOP(true, false, NULL); 1543 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", 1544 DNBStateAsString(state)); 1545 errno = 0; 1546 DNBLog("Sending ptrace PT_KILL to terminate inferior process pid %d.", m_pid); 1547 ::ptrace(PT_KILL, m_pid, 0, 0); 1548 DNBError err; 1549 err.SetErrorToErrno(); 1550 if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail()) { 1551 err.LogThreaded("MachProcess::Kill() DoSIGSTOP() ::ptrace " 1552 "(PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", 1553 m_pid, err.Status(), err.AsString()); 1554 } 1555 m_thread_actions = DNBThreadResumeActions(eStateRunning, 0); 1556 PrivateResume(); 1557 1558 // Try and reap the process without touching our m_events since 1559 // we want the code above this to still get the eStateExited event 1560 const uint32_t reap_timeout_usec = 1561 1000000; // Wait 1 second and try to reap the process 1562 const uint32_t reap_interval_usec = 10000; // 1563 uint32_t reap_time_elapsed; 1564 for (reap_time_elapsed = 0; reap_time_elapsed < reap_timeout_usec; 1565 reap_time_elapsed += reap_interval_usec) { 1566 if (GetState() == eStateExited) 1567 break; 1568 usleep(reap_interval_usec); 1569 } 1570 DNBLog("Waited %u ms for process to be reaped (state = %s)", 1571 reap_time_elapsed / 1000, DNBStateAsString(GetState())); 1572 return true; 1573} 1574 1575bool MachProcess::Interrupt() { 1576 nub_state_t state = GetState(); 1577 if (IsRunning(state)) { 1578 if (m_sent_interrupt_signo == 0) { 1579 m_sent_interrupt_signo = SIGSTOP; 1580 if (Signal(m_sent_interrupt_signo)) { 1581 DNBLogThreadedIf( 1582 LOG_PROCESS, 1583 "MachProcess::Interrupt() - sent %i signal to interrupt process", 1584 m_sent_interrupt_signo); 1585 return true; 1586 } else { 1587 m_sent_interrupt_signo = 0; 1588 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - failed to " 1589 "send %i signal to interrupt process", 1590 m_sent_interrupt_signo); 1591 } 1592 } else { 1593 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - previously " 1594 "sent an interrupt signal %i that hasn't " 1595 "been received yet, interrupt aborted", 1596 m_sent_interrupt_signo); 1597 } 1598 } else { 1599 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - process already " 1600 "stopped, no interrupt sent"); 1601 } 1602 return false; 1603} 1604 1605bool MachProcess::Signal(int signal, const struct timespec *timeout_abstime) { 1606 DNBLogThreadedIf(LOG_PROCESS, 1607 "MachProcess::Signal (signal = %d, timeout = %p)", signal, 1608 static_cast<const void *>(timeout_abstime)); 1609 nub_state_t state = GetState(); 1610 if (::kill(ProcessID(), signal) == 0) { 1611 // If we were running and we have a timeout, wait for the signal to stop 1612 if (IsRunning(state) && timeout_abstime) { 1613 DNBLogThreadedIf(LOG_PROCESS, 1614 "MachProcess::Signal (signal = %d, timeout " 1615 "= %p) waiting for signal to stop " 1616 "process...", 1617 signal, static_cast<const void *>(timeout_abstime)); 1618 m_private_events.WaitForSetEvents(eEventProcessStoppedStateChanged, 1619 timeout_abstime); 1620 state = GetState(); 1621 DNBLogThreadedIf( 1622 LOG_PROCESS, 1623 "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, 1624 static_cast<const void *>(timeout_abstime), DNBStateAsString(state)); 1625 return !IsRunning(state); 1626 } 1627 DNBLogThreadedIf( 1628 LOG_PROCESS, 1629 "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", 1630 signal, static_cast<const void *>(timeout_abstime)); 1631 return true; 1632 } 1633 DNBError err(errno, DNBError::POSIX); 1634 err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal); 1635 return false; 1636} 1637 1638bool MachProcess::SendEvent(const char *event, DNBError &send_err) { 1639 DNBLogThreadedIf(LOG_PROCESS, 1640 "MachProcess::SendEvent (event = %s) to pid: %d", event, 1641 m_pid); 1642 if (m_pid == INVALID_NUB_PROCESS) 1643 return false; 1644// FIXME: Shouldn't we use the launch flavor we were started with? 1645#if defined(WITH_FBS) || defined(WITH_BKS) 1646 return BoardServiceSendEvent(event, send_err); 1647#endif 1648 return true; 1649} 1650 1651nub_state_t MachProcess::DoSIGSTOP(bool clear_bps_and_wps, bool allow_running, 1652 uint32_t *thread_idx_ptr) { 1653 nub_state_t state = GetState(); 1654 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", 1655 DNBStateAsString(state)); 1656 1657 if (!IsRunning(state)) { 1658 if (clear_bps_and_wps) { 1659 DisableAllBreakpoints(true); 1660 DisableAllWatchpoints(true); 1661 clear_bps_and_wps = false; 1662 } 1663 1664 // If we already have a thread stopped due to a SIGSTOP, we don't have 1665 // to do anything... 1666 uint32_t thread_idx = 1667 m_thread_list.GetThreadIndexForThreadStoppedWithSignal(SIGSTOP); 1668 if (thread_idx_ptr) 1669 *thread_idx_ptr = thread_idx; 1670 if (thread_idx != UINT32_MAX) 1671 return GetState(); 1672 1673 // No threads were stopped with a SIGSTOP, we need to run and halt the 1674 // process with a signal 1675 DNBLogThreadedIf(LOG_PROCESS, 1676 "MachProcess::DoSIGSTOP() state = %s -- resuming process", 1677 DNBStateAsString(state)); 1678 if (allow_running) 1679 m_thread_actions = DNBThreadResumeActions(eStateRunning, 0); 1680 else 1681 m_thread_actions = DNBThreadResumeActions(eStateSuspended, 0); 1682 1683 PrivateResume(); 1684 1685 // Reset the event that says we were indeed running 1686 m_events.ResetEvents(eEventProcessRunningStateChanged); 1687 state = GetState(); 1688 } 1689 1690 // We need to be stopped in order to be able to detach, so we need 1691 // to send ourselves a SIGSTOP 1692 1693 DNBLogThreadedIf(LOG_PROCESS, 1694 "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", 1695 DNBStateAsString(state)); 1696 struct timespec sigstop_timeout; 1697 DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0); 1698 Signal(SIGSTOP, &sigstop_timeout); 1699 if (clear_bps_and_wps) { 1700 DisableAllBreakpoints(true); 1701 DisableAllWatchpoints(true); 1702 // clear_bps_and_wps = false; 1703 } 1704 uint32_t thread_idx = 1705 m_thread_list.GetThreadIndexForThreadStoppedWithSignal(SIGSTOP); 1706 if (thread_idx_ptr) 1707 *thread_idx_ptr = thread_idx; 1708 return GetState(); 1709} 1710 1711bool MachProcess::Detach() { 1712 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()"); 1713 1714 uint32_t thread_idx = UINT32_MAX; 1715 nub_state_t state = DoSIGSTOP(true, true, &thread_idx); 1716 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", 1717 DNBStateAsString(state)); 1718 1719 { 1720 m_thread_actions.Clear(); 1721 m_activities.Clear(); 1722 DNBThreadResumeAction thread_action; 1723 thread_action.tid = m_thread_list.ThreadIDAtIndex(thread_idx); 1724 thread_action.state = eStateRunning; 1725 thread_action.signal = -1; 1726 thread_action.addr = INVALID_NUB_ADDRESS; 1727 1728 m_thread_actions.Append(thread_action); 1729 m_thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0); 1730 1731 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 1732 1733 ReplyToAllExceptions(); 1734 } 1735 1736 m_task.ShutDownExcecptionThread(); 1737 1738 // Detach from our process 1739 errno = 0; 1740 nub_process_t pid = m_pid; 1741 int ret = ::ptrace(PT_DETACH, pid, (caddr_t)1, 0); 1742 DNBError err(errno, DNBError::POSIX); 1743 if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0)) 1744 err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); 1745 1746 // Resume our task 1747 m_task.Resume(); 1748 1749 // NULL our task out as we have already restored all exception ports 1750 m_task.Clear(); 1751 m_platform = 0; 1752 1753 // Clear out any notion of the process we once were 1754 const bool detaching = true; 1755 Clear(detaching); 1756 1757 SetState(eStateDetached); 1758 1759 return true; 1760} 1761 1762//---------------------------------------------------------------------- 1763// ReadMemory from the MachProcess level will always remove any software 1764// breakpoints from the memory buffer before returning. If you wish to 1765// read memory and see those traps, read from the MachTask 1766// (m_task.ReadMemory()) as that version will give you what is actually 1767// in inferior memory. 1768//---------------------------------------------------------------------- 1769nub_size_t MachProcess::ReadMemory(nub_addr_t addr, nub_size_t size, 1770 void *buf) { 1771 // We need to remove any current software traps (enabled software 1772 // breakpoints) that we may have placed in our tasks memory. 1773 1774 // First just read the memory as is 1775 nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf); 1776 1777 // Then place any opcodes that fall into this range back into the buffer 1778 // before we return this to callers. 1779 if (bytes_read > 0) 1780 m_breakpoints.RemoveTrapsFromBuffer(addr, bytes_read, buf); 1781 return bytes_read; 1782} 1783 1784//---------------------------------------------------------------------- 1785// WriteMemory from the MachProcess level will always write memory around 1786// any software breakpoints. Any software breakpoints will have their 1787// opcodes modified if they are enabled. Any memory that doesn't overlap 1788// with software breakpoints will be written to. If you wish to write to 1789// inferior memory without this interference, then write to the MachTask 1790// (m_task.WriteMemory()) as that version will always modify inferior 1791// memory. 1792//---------------------------------------------------------------------- 1793nub_size_t MachProcess::WriteMemory(nub_addr_t addr, nub_size_t size, 1794 const void *buf) { 1795 // We need to write any data that would go where any current software traps 1796 // (enabled software breakpoints) any software traps (breakpoints) that we 1797 // may have placed in our tasks memory. 1798 1799 std::vector<DNBBreakpoint *> bps; 1800 1801 const size_t num_bps = 1802 m_breakpoints.FindBreakpointsThatOverlapRange(addr, size, bps); 1803 if (num_bps == 0) 1804 return m_task.WriteMemory(addr, size, buf); 1805 1806 nub_size_t bytes_written = 0; 1807 nub_addr_t intersect_addr; 1808 nub_size_t intersect_size; 1809 nub_size_t opcode_offset; 1810 const uint8_t *ubuf = (const uint8_t *)buf; 1811 1812 for (size_t i = 0; i < num_bps; ++i) { 1813 DNBBreakpoint *bp = bps[i]; 1814 1815 const bool intersects = bp->IntersectsRange( 1816 addr, size, &intersect_addr, &intersect_size, &opcode_offset); 1817 UNUSED_IF_ASSERT_DISABLED(intersects); 1818 assert(intersects); 1819 assert(addr <= intersect_addr && intersect_addr < addr + size); 1820 assert(addr < intersect_addr + intersect_size && 1821 intersect_addr + intersect_size <= addr + size); 1822 assert(opcode_offset + intersect_size <= bp->ByteSize()); 1823 1824 // Check for bytes before this breakpoint 1825 const nub_addr_t curr_addr = addr + bytes_written; 1826 if (intersect_addr > curr_addr) { 1827 // There are some bytes before this breakpoint that we need to 1828 // just write to memory 1829 nub_size_t curr_size = intersect_addr - curr_addr; 1830 nub_size_t curr_bytes_written = 1831 m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written); 1832 bytes_written += curr_bytes_written; 1833 if (curr_bytes_written != curr_size) { 1834 // We weren't able to write all of the requested bytes, we 1835 // are done looping and will return the number of bytes that 1836 // we have written so far. 1837 break; 1838 } 1839 } 1840 1841 // Now write any bytes that would cover up any software breakpoints 1842 // directly into the breakpoint opcode buffer 1843 ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, 1844 intersect_size); 1845 bytes_written += intersect_size; 1846 } 1847 1848 // Write any remaining bytes after the last breakpoint if we have any left 1849 if (bytes_written < size) 1850 bytes_written += m_task.WriteMemory( 1851 addr + bytes_written, size - bytes_written, ubuf + bytes_written); 1852 1853 return bytes_written; 1854} 1855 1856void MachProcess::ReplyToAllExceptions() { 1857 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 1858 if (!m_exception_messages.empty()) { 1859 MachException::Message::iterator pos; 1860 MachException::Message::iterator begin = m_exception_messages.begin(); 1861 MachException::Message::iterator end = m_exception_messages.end(); 1862 for (pos = begin; pos != end; ++pos) { 1863 DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %u...", 1864 (uint32_t)std::distance(begin, pos)); 1865 int thread_reply_signal = 0; 1866 1867 nub_thread_t tid = 1868 m_thread_list.GetThreadIDByMachPortNumber(pos->state.thread_port); 1869 const DNBThreadResumeAction *action = NULL; 1870 if (tid != INVALID_NUB_THREAD) { 1871 action = m_thread_actions.GetActionForThread(tid, false); 1872 } 1873 1874 if (action) { 1875 thread_reply_signal = action->signal; 1876 if (thread_reply_signal) 1877 m_thread_actions.SetSignalHandledForThread(tid); 1878 } 1879 1880 DNBError err(pos->Reply(this, thread_reply_signal)); 1881 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 1882 err.LogThreadedIfError("Error replying to exception"); 1883 } 1884 1885 // Erase all exception message as we should have used and replied 1886 // to them all already. 1887 m_exception_messages.clear(); 1888 } 1889} 1890void MachProcess::PrivateResume() { 1891 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 1892 1893 m_auto_resume_signo = m_sent_interrupt_signo; 1894 if (m_auto_resume_signo) 1895 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x " 1896 "resuming (with unhandled interrupt signal " 1897 "%i)...", 1898 m_task.TaskPort(), m_auto_resume_signo); 1899 else 1900 DNBLogThreadedIf(LOG_PROCESS, 1901 "MachProcess::PrivateResume() - task 0x%x resuming...", 1902 m_task.TaskPort()); 1903 1904 ReplyToAllExceptions(); 1905 // bool stepOverBreakInstruction = step; 1906 1907 // Let the thread prepare to resume and see if any threads want us to 1908 // step over a breakpoint instruction (ProcessWillResume will modify 1909 // the value of stepOverBreakInstruction). 1910 m_thread_list.ProcessWillResume(this, m_thread_actions); 1911 1912 // Set our state accordingly 1913 if (m_thread_actions.NumActionsWithState(eStateStepping)) 1914 SetState(eStateStepping); 1915 else 1916 SetState(eStateRunning); 1917 1918 // Now resume our task. 1919 m_task.Resume(); 1920} 1921 1922DNBBreakpoint *MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, 1923 bool hardware) { 1924 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = " 1925 "0x%8.8llx, length = %llu, hardware = %i)", 1926 (uint64_t)addr, (uint64_t)length, hardware); 1927 1928 DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); 1929 if (bp) 1930 bp->Retain(); 1931 else 1932 bp = m_breakpoints.Add(addr, length, hardware); 1933 1934 if (EnableBreakpoint(addr)) { 1935 DNBLogThreadedIf(LOG_BREAKPOINTS, 1936 "MachProcess::CreateBreakpoint ( addr = " 1937 "0x%8.8llx, length = %llu) => %p", 1938 (uint64_t)addr, (uint64_t)length, static_cast<void *>(bp)); 1939 return bp; 1940 } else if (bp->Release() == 0) { 1941 m_breakpoints.Remove(addr); 1942 } 1943 // We failed to enable the breakpoint 1944 return NULL; 1945} 1946 1947DNBBreakpoint *MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, 1948 uint32_t watch_flags, 1949 bool hardware) { 1950 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = " 1951 "0x%8.8llx, length = %llu, flags = " 1952 "0x%8.8x, hardware = %i)", 1953 (uint64_t)addr, (uint64_t)length, watch_flags, hardware); 1954 1955 DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); 1956 // since the Z packets only send an address, we can only have one watchpoint 1957 // at 1958 // an address. If there is already one, we must refuse to create another 1959 // watchpoint 1960 if (wp) 1961 return NULL; 1962 1963 wp = m_watchpoints.Add(addr, length, hardware); 1964 wp->SetIsWatchpoint(watch_flags); 1965 1966 if (EnableWatchpoint(addr)) { 1967 DNBLogThreadedIf(LOG_WATCHPOINTS, 1968 "MachProcess::CreateWatchpoint ( addr = " 1969 "0x%8.8llx, length = %llu) => %p", 1970 (uint64_t)addr, (uint64_t)length, static_cast<void *>(wp)); 1971 return wp; 1972 } else { 1973 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = " 1974 "0x%8.8llx, length = %llu) => FAILED", 1975 (uint64_t)addr, (uint64_t)length); 1976 m_watchpoints.Remove(addr); 1977 } 1978 // We failed to enable the watchpoint 1979 return NULL; 1980} 1981 1982void MachProcess::DisableAllBreakpoints(bool remove) { 1983 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", 1984 __FUNCTION__, remove); 1985 1986 m_breakpoints.DisableAllBreakpoints(this); 1987 1988 if (remove) 1989 m_breakpoints.RemoveDisabled(); 1990} 1991 1992void MachProcess::DisableAllWatchpoints(bool remove) { 1993 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", 1994 __FUNCTION__, remove); 1995 1996 m_watchpoints.DisableAllWatchpoints(this); 1997 1998 if (remove) 1999 m_watchpoints.RemoveDisabled(); 2000} 2001 2002bool MachProcess::DisableBreakpoint(nub_addr_t addr, bool remove) { 2003 DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); 2004 if (bp) { 2005 // After "exec" we might end up with a bunch of breakpoints that were 2006 // disabled 2007 // manually, just ignore them 2008 if (!bp->IsEnabled()) { 2009 // Breakpoint might have been disabled by an exec 2010 if (remove && bp->Release() == 0) { 2011 m_thread_list.NotifyBreakpointChanged(bp); 2012 m_breakpoints.Remove(addr); 2013 } 2014 return true; 2015 } 2016 2017 // We have multiple references to this breakpoint, decrement the ref count 2018 // and if it isn't zero, then return true; 2019 if (remove && bp->Release() > 0) 2020 return true; 2021 2022 DNBLogThreadedIf( 2023 LOG_BREAKPOINTS | LOG_VERBOSE, 2024 "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d )", 2025 (uint64_t)addr, remove); 2026 2027 if (bp->IsHardware()) { 2028 bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint(bp); 2029 2030 if (hw_disable_result) { 2031 bp->SetEnabled(false); 2032 // Let the thread list know that a breakpoint has been modified 2033 if (remove) { 2034 m_thread_list.NotifyBreakpointChanged(bp); 2035 m_breakpoints.Remove(addr); 2036 } 2037 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( " 2038 "addr = 0x%8.8llx, remove = %d ) " 2039 "(hardware) => success", 2040 (uint64_t)addr, remove); 2041 return true; 2042 } 2043 2044 return false; 2045 } 2046 2047 const nub_size_t break_op_size = bp->ByteSize(); 2048 assert(break_op_size > 0); 2049 const uint8_t *const break_op = 2050 DNBArchProtocol::GetBreakpointOpcode(bp->ByteSize()); 2051 if (break_op_size > 0) { 2052 // Clear a software breakpoint instruction 2053 uint8_t curr_break_op[break_op_size]; 2054 bool break_op_found = false; 2055 2056 // Read the breakpoint opcode 2057 if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == 2058 break_op_size) { 2059 bool verify = false; 2060 if (bp->IsEnabled()) { 2061 // Make sure a breakpoint opcode exists at this address 2062 if (memcmp(curr_break_op, break_op, break_op_size) == 0) { 2063 break_op_found = true; 2064 // We found a valid breakpoint opcode at this address, now restore 2065 // the saved opcode. 2066 if (m_task.WriteMemory(addr, break_op_size, 2067 bp->SavedOpcodeBytes()) == break_op_size) { 2068 verify = true; 2069 } else { 2070 DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, " 2071 "remove = %d ) memory write failed when restoring " 2072 "original opcode", 2073 (uint64_t)addr, remove); 2074 } 2075 } else { 2076 DNBLogWarning("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, " 2077 "remove = %d ) expected a breakpoint opcode but " 2078 "didn't find one.", 2079 (uint64_t)addr, remove); 2080 // Set verify to true and so we can check if the original opcode has 2081 // already been restored 2082 verify = true; 2083 } 2084 } else { 2085 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, 2086 "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, " 2087 "remove = %d ) is not enabled", 2088 (uint64_t)addr, remove); 2089 // Set verify to true and so we can check if the original opcode is 2090 // there 2091 verify = true; 2092 } 2093 2094 if (verify) { 2095 uint8_t verify_opcode[break_op_size]; 2096 // Verify that our original opcode made it back to the inferior 2097 if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == 2098 break_op_size) { 2099 // compare the memory we just read with the original opcode 2100 if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 2101 0) { 2102 // SUCCESS 2103 bp->SetEnabled(false); 2104 // Let the thread list know that a breakpoint has been modified 2105 if (remove && bp->Release() == 0) { 2106 m_thread_list.NotifyBreakpointChanged(bp); 2107 m_breakpoints.Remove(addr); 2108 } 2109 DNBLogThreadedIf(LOG_BREAKPOINTS, 2110 "MachProcess::DisableBreakpoint ( addr = " 2111 "0x%8.8llx, remove = %d ) => success", 2112 (uint64_t)addr, remove); 2113 return true; 2114 } else { 2115 if (break_op_found) 2116 DNBLogError("MachProcess::DisableBreakpoint ( addr = " 2117 "0x%8.8llx, remove = %d ) : failed to restore " 2118 "original opcode", 2119 (uint64_t)addr, remove); 2120 else 2121 DNBLogError("MachProcess::DisableBreakpoint ( addr = " 2122 "0x%8.8llx, remove = %d ) : opcode changed", 2123 (uint64_t)addr, remove); 2124 } 2125 } else { 2126 DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable " 2127 "breakpoint 0x%8.8llx", 2128 (uint64_t)addr); 2129 } 2130 } 2131 } else { 2132 DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory " 2133 "at 0x%8.8llx", 2134 (uint64_t)addr); 2135 } 2136 } 2137 } else { 2138 DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = " 2139 "%d ) invalid breakpoint address", 2140 (uint64_t)addr, remove); 2141 } 2142 return false; 2143} 2144 2145bool MachProcess::DisableWatchpoint(nub_addr_t addr, bool remove) { 2146 DNBLogThreadedIf(LOG_WATCHPOINTS, 2147 "MachProcess::%s(addr = 0x%8.8llx, remove = %d)", 2148 __FUNCTION__, (uint64_t)addr, remove); 2149 DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); 2150 if (wp) { 2151 // If we have multiple references to a watchpoint, removing the watchpoint 2152 // shouldn't clear it 2153 if (remove && wp->Release() > 0) 2154 return true; 2155 2156 nub_addr_t addr = wp->Address(); 2157 DNBLogThreadedIf( 2158 LOG_WATCHPOINTS, 2159 "MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d )", 2160 (uint64_t)addr, remove); 2161 2162 if (wp->IsHardware()) { 2163 bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint(wp); 2164 2165 if (hw_disable_result) { 2166 wp->SetEnabled(false); 2167 if (remove) 2168 m_watchpoints.Remove(addr); 2169 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( " 2170 "addr = 0x%8.8llx, remove = %d ) " 2171 "(hardware) => success", 2172 (uint64_t)addr, remove); 2173 return true; 2174 } 2175 } 2176 2177 // TODO: clear software watchpoints if we implement them 2178 } else { 2179 DNBLogError("MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = " 2180 "%d ) invalid watchpoint ID", 2181 (uint64_t)addr, remove); 2182 } 2183 return false; 2184} 2185 2186uint32_t MachProcess::GetNumSupportedHardwareWatchpoints() const { 2187 return m_thread_list.NumSupportedHardwareWatchpoints(); 2188} 2189 2190bool MachProcess::EnableBreakpoint(nub_addr_t addr) { 2191 DNBLogThreadedIf(LOG_BREAKPOINTS, 2192 "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx )", 2193 (uint64_t)addr); 2194 DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); 2195 if (bp) { 2196 if (bp->IsEnabled()) { 2197 DNBLogWarning("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): " 2198 "breakpoint already enabled.", 2199 (uint64_t)addr); 2200 return true; 2201 } else { 2202 if (bp->HardwarePreferred()) { 2203 bp->SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp)); 2204 if (bp->IsHardware()) { 2205 bp->SetEnabled(true); 2206 return true; 2207 } 2208 } 2209 2210 const nub_size_t break_op_size = bp->ByteSize(); 2211 assert(break_op_size != 0); 2212 const uint8_t *const break_op = 2213 DNBArchProtocol::GetBreakpointOpcode(break_op_size); 2214 if (break_op_size > 0) { 2215 // Save the original opcode by reading it 2216 if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == 2217 break_op_size) { 2218 // Write a software breakpoint in place of the original opcode 2219 if (m_task.WriteMemory(addr, break_op_size, break_op) == 2220 break_op_size) { 2221 uint8_t verify_break_op[4]; 2222 if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == 2223 break_op_size) { 2224 if (memcmp(break_op, verify_break_op, break_op_size) == 0) { 2225 bp->SetEnabled(true); 2226 // Let the thread list know that a breakpoint has been modified 2227 m_thread_list.NotifyBreakpointChanged(bp); 2228 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::" 2229 "EnableBreakpoint ( addr = " 2230 "0x%8.8llx ) : SUCCESS.", 2231 (uint64_t)addr); 2232 return true; 2233 } else { 2234 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx " 2235 "): breakpoint opcode verification failed.", 2236 (uint64_t)addr); 2237 } 2238 } else { 2239 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): " 2240 "unable to read memory to verify breakpoint opcode.", 2241 (uint64_t)addr); 2242 } 2243 } else { 2244 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): " 2245 "unable to write breakpoint opcode to memory.", 2246 (uint64_t)addr); 2247 } 2248 } else { 2249 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): " 2250 "unable to read memory at breakpoint address.", 2251 (uint64_t)addr); 2252 } 2253 } else { 2254 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) no " 2255 "software breakpoint opcode for current architecture.", 2256 (uint64_t)addr); 2257 } 2258 } 2259 } 2260 return false; 2261} 2262 2263bool MachProcess::EnableWatchpoint(nub_addr_t addr) { 2264 DNBLogThreadedIf(LOG_WATCHPOINTS, 2265 "MachProcess::EnableWatchpoint(addr = 0x%8.8llx)", 2266 (uint64_t)addr); 2267 DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); 2268 if (wp) { 2269 nub_addr_t addr = wp->Address(); 2270 if (wp->IsEnabled()) { 2271 DNBLogWarning("MachProcess::EnableWatchpoint(addr = 0x%8.8llx): " 2272 "watchpoint already enabled.", 2273 (uint64_t)addr); 2274 return true; 2275 } else { 2276 // Currently only try and set hardware watchpoints. 2277 wp->SetHardwareIndex(m_thread_list.EnableHardwareWatchpoint(wp)); 2278 if (wp->IsHardware()) { 2279 wp->SetEnabled(true); 2280 return true; 2281 } 2282 // TODO: Add software watchpoints by doing page protection tricks. 2283 } 2284 } 2285 return false; 2286} 2287 2288// Called by the exception thread when an exception has been received from 2289// our process. The exception message is completely filled and the exception 2290// data has already been copied. 2291void MachProcess::ExceptionMessageReceived( 2292 const MachException::Message &exceptionMessage) { 2293 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 2294 2295 if (m_exception_messages.empty()) 2296 m_task.Suspend(); 2297 2298 DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )"); 2299 2300 // Use a locker to automatically unlock our mutex in case of exceptions 2301 // Add the exception to our internal exception stack 2302 m_exception_messages.push_back(exceptionMessage); 2303} 2304 2305task_t MachProcess::ExceptionMessageBundleComplete() { 2306 // We have a complete bundle of exceptions for our child process. 2307 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 2308 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %llu exception messages.", 2309 __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size()); 2310 bool auto_resume = false; 2311 if (!m_exception_messages.empty()) { 2312 m_did_exec = false; 2313 // First check for any SIGTRAP and make sure we didn't exec 2314 const task_t task = m_task.TaskPort(); 2315 size_t i; 2316 if (m_pid != 0) { 2317 bool received_interrupt = false; 2318 uint32_t num_task_exceptions = 0; 2319 for (i = 0; i < m_exception_messages.size(); ++i) { 2320 if (m_exception_messages[i].state.task_port == task) { 2321 ++num_task_exceptions; 2322 const int signo = m_exception_messages[i].state.SoftSignal(); 2323 if (signo == SIGTRAP) { 2324 // SIGTRAP could mean that we exec'ed. We need to check the 2325 // dyld all_image_infos.infoArray to see if it is NULL and if 2326 // so, say that we exec'ed. 2327 const nub_addr_t aii_addr = GetDYLDAllImageInfosAddress(); 2328 if (aii_addr != INVALID_NUB_ADDRESS) { 2329 const nub_addr_t info_array_count_addr = aii_addr + 4; 2330 uint32_t info_array_count = 0; 2331 if (m_task.ReadMemory(info_array_count_addr, 4, 2332 &info_array_count) == 4) { 2333 if (info_array_count == 0) { 2334 m_did_exec = true; 2335 // Force the task port to update itself in case the task port 2336 // changed after exec 2337 DNBError err; 2338 const task_t old_task = m_task.TaskPort(); 2339 const task_t new_task = 2340 m_task.TaskPortForProcessID(err, true); 2341 if (old_task != new_task) 2342 DNBLogThreadedIf( 2343 LOG_PROCESS, 2344 "exec: task changed from 0x%4.4x to 0x%4.4x", old_task, 2345 new_task); 2346 } 2347 } else { 2348 DNBLog("error: failed to read all_image_infos.infoArrayCount " 2349 "from 0x%8.8llx", 2350 (uint64_t)info_array_count_addr); 2351 } 2352 } 2353 break; 2354 } else if (m_sent_interrupt_signo != 0 && 2355 signo == m_sent_interrupt_signo) { 2356 received_interrupt = true; 2357 } 2358 } 2359 } 2360 2361 if (m_did_exec) { 2362 cpu_type_t process_cpu_type = 2363 MachProcess::GetCPUTypeForLocalProcess(m_pid); 2364 if (m_cpu_type != process_cpu_type) { 2365 DNBLog("arch changed from 0x%8.8x to 0x%8.8x", m_cpu_type, 2366 process_cpu_type); 2367 m_cpu_type = process_cpu_type; 2368 DNBArchProtocol::SetArchitecture(process_cpu_type); 2369 } 2370 m_thread_list.Clear(); 2371 m_activities.Clear(); 2372 m_breakpoints.DisableAll(); 2373 m_task.ClearAllocations(); 2374 } 2375 2376 if (m_sent_interrupt_signo != 0) { 2377 if (received_interrupt) { 2378 DNBLogThreadedIf(LOG_PROCESS, 2379 "MachProcess::ExceptionMessageBundleComplete(): " 2380 "process successfully interrupted with signal %i", 2381 m_sent_interrupt_signo); 2382 2383 // Mark that we received the interrupt signal 2384 m_sent_interrupt_signo = 0; 2385 // Not check if we had a case where: 2386 // 1 - We called MachProcess::Interrupt() but we stopped for another 2387 // reason 2388 // 2 - We called MachProcess::Resume() (but still haven't gotten the 2389 // interrupt signal) 2390 // 3 - We are now incorrectly stopped because we are handling the 2391 // interrupt signal we missed 2392 // 4 - We might need to resume if we stopped only with the interrupt 2393 // signal that we never handled 2394 if (m_auto_resume_signo != 0) { 2395 // Only auto_resume if we stopped with _only_ the interrupt signal 2396 if (num_task_exceptions == 1) { 2397 auto_resume = true; 2398 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::" 2399 "ExceptionMessageBundleComplete(): " 2400 "auto resuming due to unhandled " 2401 "interrupt signal %i", 2402 m_auto_resume_signo); 2403 } 2404 m_auto_resume_signo = 0; 2405 } 2406 } else { 2407 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::" 2408 "ExceptionMessageBundleComplete(): " 2409 "didn't get signal %i after " 2410 "MachProcess::Interrupt()", 2411 m_sent_interrupt_signo); 2412 } 2413 } 2414 } 2415 2416 // Let all threads recover from stopping and do any clean up based 2417 // on the previous thread state (if any). 2418 m_thread_list.ProcessDidStop(this); 2419 m_activities.Clear(); 2420 2421 // Let each thread know of any exceptions 2422 for (i = 0; i < m_exception_messages.size(); ++i) { 2423 // Let the thread list figure use the MachProcess to forward all 2424 // exceptions 2425 // on down to each thread. 2426 if (m_exception_messages[i].state.task_port == task) 2427 m_thread_list.NotifyException(m_exception_messages[i].state); 2428 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 2429 m_exception_messages[i].Dump(); 2430 } 2431 2432 if (DNBLogCheckLogBit(LOG_THREAD)) 2433 m_thread_list.Dump(); 2434 2435 bool step_more = false; 2436 if (m_thread_list.ShouldStop(step_more) && !auto_resume) { 2437 // Wait for the eEventProcessRunningStateChanged event to be reset 2438 // before changing state to stopped to avoid race condition with 2439 // very fast start/stops 2440 struct timespec timeout; 2441 // DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000); // Wait for 250 2442 // ms 2443 DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms 2444 m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout); 2445 SetState(eStateStopped); 2446 } else { 2447 // Resume without checking our current state. 2448 PrivateResume(); 2449 } 2450 } else { 2451 DNBLogThreadedIf( 2452 LOG_EXCEPTIONS, "%s empty exception messages bundle (%llu exceptions).", 2453 __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size()); 2454 } 2455 return m_task.TaskPort(); 2456} 2457 2458nub_size_t 2459MachProcess::CopyImageInfos(struct DNBExecutableImageInfo **image_infos, 2460 bool only_changed) { 2461 if (m_image_infos_callback != NULL) 2462 return m_image_infos_callback(ProcessID(), image_infos, only_changed, 2463 m_image_infos_baton); 2464 return 0; 2465} 2466 2467void MachProcess::SharedLibrariesUpdated() { 2468 uint32_t event_bits = eEventSharedLibsStateChange; 2469 // Set the shared library event bit to let clients know of shared library 2470 // changes 2471 m_events.SetEvents(event_bits); 2472 // Wait for the event bit to reset if a reset ACK is requested 2473 m_events.WaitForResetAck(event_bits); 2474} 2475 2476void MachProcess::SetExitInfo(const char *info) { 2477 if (info && info[0]) { 2478 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(\"%s\")", __FUNCTION__, 2479 info); 2480 m_exit_info.assign(info); 2481 } else { 2482 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(NULL)", __FUNCTION__); 2483 m_exit_info.clear(); 2484 } 2485} 2486 2487void MachProcess::AppendSTDOUT(char *s, size_t len) { 2488 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%llu> %s) ...", __FUNCTION__, 2489 (uint64_t)len, s); 2490 PTHREAD_MUTEX_LOCKER(locker, m_stdio_mutex); 2491 m_stdout_data.append(s, len); 2492 m_events.SetEvents(eEventStdioAvailable); 2493 2494 // Wait for the event bit to reset if a reset ACK is requested 2495 m_events.WaitForResetAck(eEventStdioAvailable); 2496} 2497 2498size_t MachProcess::GetAvailableSTDOUT(char *buf, size_t buf_size) { 2499 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, 2500 static_cast<void *>(buf), (uint64_t)buf_size); 2501 PTHREAD_MUTEX_LOCKER(locker, m_stdio_mutex); 2502 size_t bytes_available = m_stdout_data.size(); 2503 if (bytes_available > 0) { 2504 if (bytes_available > buf_size) { 2505 memcpy(buf, m_stdout_data.data(), buf_size); 2506 m_stdout_data.erase(0, buf_size); 2507 bytes_available = buf_size; 2508 } else { 2509 memcpy(buf, m_stdout_data.data(), bytes_available); 2510 m_stdout_data.clear(); 2511 } 2512 } 2513 return bytes_available; 2514} 2515 2516nub_addr_t MachProcess::GetDYLDAllImageInfosAddress() { 2517 DNBError err; 2518 return m_task.GetDYLDAllImageInfosAddress(err); 2519} 2520 2521/// From dyld SPI header dyld_process_info.h 2522struct dyld_process_state_info { 2523 uint64_t timestamp; 2524 uint32_t imageCount; 2525 uint32_t initialImageCount; 2526 // one of dyld_process_state_* values 2527 uint8_t dyldState; 2528}; 2529enum { 2530 dyld_process_state_not_started = 0x00, 2531 dyld_process_state_dyld_initialized = 0x10, 2532 dyld_process_state_terminated_before_inits = 0x20, 2533 dyld_process_state_libSystem_initialized = 0x30, 2534 dyld_process_state_running_initializers = 0x40, 2535 dyld_process_state_program_running = 0x50, 2536 dyld_process_state_dyld_terminated = 0x60 2537}; 2538 2539JSONGenerator::ObjectSP MachProcess::GetDyldProcessState() { 2540 JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary()); 2541 if (!m_dyld_process_info_get_state) { 2542 reply_sp->AddStringItem("error", 2543 "_dyld_process_info_get_state unavailable"); 2544 return reply_sp; 2545 } 2546 if (!m_dyld_process_info_create) { 2547 reply_sp->AddStringItem("error", "_dyld_process_info_create unavailable"); 2548 return reply_sp; 2549 } 2550 2551 kern_return_t kern_ret; 2552 dyld_process_info info = 2553 m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret); 2554 if (!info || kern_ret != KERN_SUCCESS) { 2555 reply_sp->AddStringItem( 2556 "error", "Unable to create dyld_process_info for inferior task"); 2557 return reply_sp; 2558 } 2559 2560 struct dyld_process_state_info state_info; 2561 m_dyld_process_info_get_state(info, &state_info); 2562 reply_sp->AddIntegerItem("process_state_value", state_info.dyldState); 2563 switch (state_info.dyldState) { 2564 case dyld_process_state_not_started: 2565 reply_sp->AddStringItem("process_state string", 2566 "dyld_process_state_not_started"); 2567 break; 2568 case dyld_process_state_dyld_initialized: 2569 reply_sp->AddStringItem("process_state string", 2570 "dyld_process_state_dyld_initialized"); 2571 break; 2572 case dyld_process_state_terminated_before_inits: 2573 reply_sp->AddStringItem("process_state string", 2574 "dyld_process_state_terminated_before_inits"); 2575 break; 2576 case dyld_process_state_libSystem_initialized: 2577 reply_sp->AddStringItem("process_state string", 2578 "dyld_process_state_libSystem_initialized"); 2579 break; 2580 case dyld_process_state_running_initializers: 2581 reply_sp->AddStringItem("process_state string", 2582 "dyld_process_state_running_initializers"); 2583 break; 2584 case dyld_process_state_program_running: 2585 reply_sp->AddStringItem("process_state string", 2586 "dyld_process_state_program_running"); 2587 break; 2588 case dyld_process_state_dyld_terminated: 2589 reply_sp->AddStringItem("process_state string", 2590 "dyld_process_state_dyld_terminated"); 2591 break; 2592 }; 2593 2594 m_dyld_process_info_release(info); 2595 2596 return reply_sp; 2597} 2598 2599size_t MachProcess::GetAvailableSTDERR(char *buf, size_t buf_size) { return 0; } 2600 2601void *MachProcess::STDIOThread(void *arg) { 2602 MachProcess *proc = (MachProcess *)arg; 2603 DNBLogThreadedIf(LOG_PROCESS, 2604 "MachProcess::%s ( arg = %p ) thread starting...", 2605 __FUNCTION__, arg); 2606 2607#if defined(__APPLE__) 2608 pthread_setname_np("stdio monitoring thread"); 2609#endif 2610 2611 // We start use a base and more options so we can control if we 2612 // are currently using a timeout on the mach_msg. We do this to get a 2613 // bunch of related exceptions on our exception port so we can process 2614 // then together. When we have multiple threads, we can get an exception 2615 // per thread and they will come in consecutively. The main thread loop 2616 // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT 2617 // flag set in the options, so we will wait forever for an exception on 2618 // our exception port. After we get one exception, we then will use the 2619 // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current 2620 // exceptions for our process. After we have received the last pending 2621 // exception, we will get a timeout which enables us to then notify 2622 // our main thread that we have an exception bundle available. We then wait 2623 // for the main thread to tell this exception thread to start trying to get 2624 // exceptions messages again and we start again with a mach_msg read with 2625 // infinite timeout. 2626 DNBError err; 2627 int stdout_fd = proc->GetStdoutFileDescriptor(); 2628 int stderr_fd = proc->GetStderrFileDescriptor(); 2629 if (stdout_fd == stderr_fd) 2630 stderr_fd = -1; 2631 2632 while (stdout_fd >= 0 || stderr_fd >= 0) { 2633 ::pthread_testcancel(); 2634 2635 fd_set read_fds; 2636 FD_ZERO(&read_fds); 2637 if (stdout_fd >= 0) 2638 FD_SET(stdout_fd, &read_fds); 2639 if (stderr_fd >= 0) 2640 FD_SET(stderr_fd, &read_fds); 2641 int nfds = std::max<int>(stdout_fd, stderr_fd) + 1; 2642 2643 int num_set_fds = select(nfds, &read_fds, NULL, NULL, NULL); 2644 DNBLogThreadedIf(LOG_PROCESS, 2645 "select (nfds, &read_fds, NULL, NULL, NULL) => %d", 2646 num_set_fds); 2647 2648 if (num_set_fds < 0) { 2649 int select_errno = errno; 2650 if (DNBLogCheckLogBit(LOG_PROCESS)) { 2651 err.SetError(select_errno, DNBError::POSIX); 2652 err.LogThreadedIfError( 2653 "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); 2654 } 2655 2656 switch (select_errno) { 2657 case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate 2658 // the requested number of file descriptors, or we have 2659 // non-blocking IO 2660 break; 2661 case EBADF: // One of the descriptor sets specified an invalid descriptor. 2662 return NULL; 2663 break; 2664 case EINTR: // A signal was delivered before the time limit expired and 2665 // before any of the selected events occurred. 2666 case EINVAL: // The specified time limit is invalid. One of its components 2667 // is negative or too large. 2668 default: // Other unknown error 2669 break; 2670 } 2671 } else if (num_set_fds == 0) { 2672 } else { 2673 char s[1024]; 2674 s[sizeof(s) - 1] = '\0'; // Ensure we have NULL termination 2675 ssize_t bytes_read = 0; 2676 if (stdout_fd >= 0 && FD_ISSET(stdout_fd, &read_fds)) { 2677 do { 2678 bytes_read = ::read(stdout_fd, s, sizeof(s) - 1); 2679 if (bytes_read < 0) { 2680 int read_errno = errno; 2681 DNBLogThreadedIf(LOG_PROCESS, 2682 "read (stdout_fd, ) => %zd errno: %d (%s)", 2683 bytes_read, read_errno, strerror(read_errno)); 2684 } else if (bytes_read == 0) { 2685 // EOF... 2686 DNBLogThreadedIf( 2687 LOG_PROCESS, 2688 "read (stdout_fd, ) => %zd (reached EOF for child STDOUT)", 2689 bytes_read); 2690 stdout_fd = -1; 2691 } else if (bytes_read > 0) { 2692 proc->AppendSTDOUT(s, bytes_read); 2693 } 2694 2695 } while (bytes_read > 0); 2696 } 2697 2698 if (stderr_fd >= 0 && FD_ISSET(stderr_fd, &read_fds)) { 2699 do { 2700 bytes_read = ::read(stderr_fd, s, sizeof(s) - 1); 2701 if (bytes_read < 0) { 2702 int read_errno = errno; 2703 DNBLogThreadedIf(LOG_PROCESS, 2704 "read (stderr_fd, ) => %zd errno: %d (%s)", 2705 bytes_read, read_errno, strerror(read_errno)); 2706 } else if (bytes_read == 0) { 2707 // EOF... 2708 DNBLogThreadedIf( 2709 LOG_PROCESS, 2710 "read (stderr_fd, ) => %zd (reached EOF for child STDERR)", 2711 bytes_read); 2712 stderr_fd = -1; 2713 } else if (bytes_read > 0) { 2714 proc->AppendSTDOUT(s, bytes_read); 2715 } 2716 2717 } while (bytes_read > 0); 2718 } 2719 } 2720 } 2721 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", 2722 __FUNCTION__, arg); 2723 return NULL; 2724} 2725 2726void MachProcess::SignalAsyncProfileData(const char *info) { 2727 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%s) ...", __FUNCTION__, info); 2728 PTHREAD_MUTEX_LOCKER(locker, m_profile_data_mutex); 2729 m_profile_data.push_back(info); 2730 m_events.SetEvents(eEventProfileDataAvailable); 2731 2732 // Wait for the event bit to reset if a reset ACK is requested 2733 m_events.WaitForResetAck(eEventProfileDataAvailable); 2734} 2735 2736size_t MachProcess::GetAsyncProfileData(char *buf, size_t buf_size) { 2737 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, 2738 static_cast<void *>(buf), (uint64_t)buf_size); 2739 PTHREAD_MUTEX_LOCKER(locker, m_profile_data_mutex); 2740 if (m_profile_data.empty()) 2741 return 0; 2742 2743 size_t bytes_available = m_profile_data.front().size(); 2744 if (bytes_available > 0) { 2745 if (bytes_available > buf_size) { 2746 memcpy(buf, m_profile_data.front().data(), buf_size); 2747 m_profile_data.front().erase(0, buf_size); 2748 bytes_available = buf_size; 2749 } else { 2750 memcpy(buf, m_profile_data.front().data(), bytes_available); 2751 m_profile_data.erase(m_profile_data.begin()); 2752 } 2753 } 2754 return bytes_available; 2755} 2756 2757void *MachProcess::ProfileThread(void *arg) { 2758 MachProcess *proc = (MachProcess *)arg; 2759 DNBLogThreadedIf(LOG_PROCESS, 2760 "MachProcess::%s ( arg = %p ) thread starting...", 2761 __FUNCTION__, arg); 2762 2763#if defined(__APPLE__) 2764 pthread_setname_np("performance profiling thread"); 2765#endif 2766 2767 while (proc->IsProfilingEnabled()) { 2768 nub_state_t state = proc->GetState(); 2769 if (state == eStateRunning) { 2770 std::string data = 2771 proc->Task().GetProfileData(proc->GetProfileScanType()); 2772 if (!data.empty()) { 2773 proc->SignalAsyncProfileData(data.c_str()); 2774 } 2775 } else if ((state == eStateUnloaded) || (state == eStateDetached) || 2776 (state == eStateUnloaded)) { 2777 // Done. Get out of this thread. 2778 break; 2779 } 2780 timespec ts; 2781 { 2782 using namespace std::chrono; 2783 std::chrono::microseconds dur(proc->ProfileInterval()); 2784 const auto dur_secs = duration_cast<seconds>(dur); 2785 const auto dur_usecs = dur % std::chrono::seconds(1); 2786 DNBTimer::OffsetTimeOfDay(&ts, dur_secs.count(), 2787 dur_usecs.count()); 2788 } 2789 uint32_t bits_set = 2790 proc->m_profile_events.WaitForSetEvents(eMachProcessProfileCancel, &ts); 2791 // If we got bits back, we were told to exit. Do so. 2792 if (bits_set & eMachProcessProfileCancel) 2793 break; 2794 } 2795 return NULL; 2796} 2797 2798pid_t MachProcess::AttachForDebug( 2799 pid_t pid, 2800 const RNBContext::IgnoredExceptions &ignored_exceptions, 2801 char *err_str, 2802 size_t err_len) { 2803 // Clear out and clean up from any current state 2804 Clear(); 2805 if (pid != 0) { 2806 DNBError err; 2807 // Make sure the process exists... 2808 if (::getpgid(pid) < 0) { 2809 err.SetErrorToErrno(); 2810 const char *err_cstr = err.AsString(); 2811 ::snprintf(err_str, err_len, "%s", 2812 err_cstr ? err_cstr : "No such process"); 2813 DNBLogError ("MachProcess::AttachForDebug pid %d does not exist", pid); 2814 return INVALID_NUB_PROCESS; 2815 } 2816 2817 SetState(eStateAttaching); 2818 m_pid = pid; 2819 if (!m_task.StartExceptionThread(ignored_exceptions, err)) { 2820 const char *err_cstr = err.AsString(); 2821 ::snprintf(err_str, err_len, "%s", 2822 err_cstr ? err_cstr : "unable to start the exception thread"); 2823 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); 2824 DNBLogError( 2825 "[LaunchAttach] END (%d) MachProcess::AttachForDebug failed to start " 2826 "exception thread attaching to pid %i: %s", 2827 getpid(), pid, err_str); 2828 m_pid = INVALID_NUB_PROCESS; 2829 return INVALID_NUB_PROCESS; 2830 } 2831 2832 DNBLog("[LaunchAttach] (%d) About to ptrace(PT_ATTACHEXC, %d)...", getpid(), 2833 pid); 2834 errno = 0; 2835 int ptrace_result = ::ptrace(PT_ATTACHEXC, pid, 0, 0); 2836 int ptrace_errno = errno; 2837 DNBLog("[LaunchAttach] (%d) Completed ptrace(PT_ATTACHEXC, %d) == %d", 2838 getpid(), pid, ptrace_result); 2839 if (ptrace_result != 0) { 2840 err.SetError(ptrace_errno); 2841 DNBLogError("MachProcess::AttachForDebug failed to ptrace(PT_ATTACHEXC) " 2842 "pid %i: %s", 2843 pid, err.AsString()); 2844 } else { 2845 err.Clear(); 2846 } 2847 2848 if (err.Success()) { 2849 m_flags |= eMachProcessFlagsAttached; 2850 // Sleep a bit to let the exception get received and set our process 2851 // status 2852 // to stopped. 2853 ::usleep(250000); 2854 DNBLog("[LaunchAttach] (%d) Done napping after ptrace(PT_ATTACHEXC)'ing", 2855 getpid()); 2856 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid); 2857 return m_pid; 2858 } else { 2859 ::snprintf(err_str, err_len, "%s", err.AsString()); 2860 DNBLogError( 2861 "[LaunchAttach] (%d) MachProcess::AttachForDebug error: failed to " 2862 "attach to pid %d", 2863 getpid(), pid); 2864 2865 if (ProcessIsBeingDebugged(pid)) { 2866 nub_process_t ppid = GetParentProcessID(pid); 2867 if (ppid == getpid()) { 2868 snprintf(err_str, err_len, 2869 "%s - Failed to attach to pid %d, AttachForDebug() " 2870 "unable to ptrace(PT_ATTACHEXC)", 2871 err.AsString(), m_pid); 2872 } else { 2873 snprintf(err_str, err_len, 2874 "%s - process %d is already being debugged by pid %d", 2875 err.AsString(), pid, ppid); 2876 DNBLogError( 2877 "[LaunchAttach] (%d) MachProcess::AttachForDebug pid %d is " 2878 "already being debugged by pid %d", 2879 getpid(), pid, ppid); 2880 } 2881 } 2882 } 2883 } 2884 return INVALID_NUB_PROCESS; 2885} 2886 2887Genealogy::ThreadActivitySP 2888MachProcess::GetGenealogyInfoForThread(nub_thread_t tid, bool &timed_out) { 2889 return m_activities.GetGenealogyInfoForThread(m_pid, tid, m_thread_list, 2890 m_task.TaskPort(), timed_out); 2891} 2892 2893Genealogy::ProcessExecutableInfoSP 2894MachProcess::GetGenealogyImageInfo(size_t idx) { 2895 return m_activities.GetProcessExecutableInfosAtIndex(idx); 2896} 2897 2898bool MachProcess::GetOSVersionNumbers(uint64_t *major, uint64_t *minor, 2899 uint64_t *patch) { 2900 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 2901 2902 NSOperatingSystemVersion vers = 2903 [[NSProcessInfo processInfo] operatingSystemVersion]; 2904 if (major) 2905 *major = vers.majorVersion; 2906 if (minor) 2907 *minor = vers.minorVersion; 2908 if (patch) 2909 *patch = vers.patchVersion; 2910 2911 [pool drain]; 2912 2913 return true; 2914} 2915 2916std::string MachProcess::GetMacCatalystVersionString() { 2917 @autoreleasepool { 2918 NSDictionary *version_info = 2919 [NSDictionary dictionaryWithContentsOfFile: 2920 @"/System/Library/CoreServices/SystemVersion.plist"]; 2921 NSString *version_value = [version_info objectForKey: @"iOSSupportVersion"]; 2922 if (const char *version_str = [version_value UTF8String]) 2923 return version_str; 2924 } 2925 return {}; 2926} 2927 2928nub_process_t MachProcess::GetParentProcessID(nub_process_t child_pid) { 2929 struct proc_bsdshortinfo proc; 2930 if (proc_pidinfo(child_pid, PROC_PIDT_SHORTBSDINFO, 0, &proc, 2931 PROC_PIDT_SHORTBSDINFO_SIZE) == sizeof(proc)) { 2932 return proc.pbsi_ppid; 2933 } 2934 return INVALID_NUB_PROCESS; 2935} 2936 2937bool MachProcess::ProcessIsBeingDebugged(nub_process_t pid) { 2938 struct kinfo_proc kinfo; 2939 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 2940 size_t len = sizeof(struct kinfo_proc); 2941 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &kinfo, &len, NULL, 0) == 0 && 2942 (kinfo.kp_proc.p_flag & P_TRACED)) 2943 return true; 2944 else 2945 return false; 2946} 2947 2948#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) || defined(WITH_FBS) 2949/// Get the app bundle from the given path. Returns the empty string if the 2950/// path doesn't appear to be an app bundle. 2951static std::string GetAppBundle(std::string path) { 2952 auto pos = path.rfind(".app"); 2953 // Path doesn't contain `.app`. 2954 if (pos == std::string::npos) 2955 return {}; 2956 // Path has `.app` extension. 2957 if (pos == path.size() - 4) 2958 return path.substr(0, pos + 4); 2959 2960 // Look for `.app` before a path separator. 2961 do { 2962 if (path[pos + 4] == '/') 2963 return path.substr(0, pos + 4); 2964 path = path.substr(0, pos); 2965 pos = path.rfind(".app"); 2966 } while (pos != std::string::npos); 2967 2968 return {}; 2969} 2970#endif 2971 2972// Do the process specific setup for attach. If this returns NULL, then there's 2973// no 2974// platform specific stuff to be done to wait for the attach. If you get 2975// non-null, 2976// pass that token to the CheckForProcess method, and then to 2977// CleanupAfterAttach. 2978 2979// Call PrepareForAttach before attaching to a process that has not yet 2980// launched 2981// This returns a token that can be passed to CheckForProcess, and to 2982// CleanupAfterAttach. 2983// You should call CleanupAfterAttach to free the token, and do whatever other 2984// cleanup seems good. 2985 2986const void *MachProcess::PrepareForAttach(const char *path, 2987 nub_launch_flavor_t launch_flavor, 2988 bool waitfor, DNBError &attach_err) { 2989#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) || defined(WITH_FBS) 2990 // Tell SpringBoard to halt the next launch of this application on startup. 2991 2992 if (!waitfor) 2993 return NULL; 2994 2995 std::string app_bundle_path = GetAppBundle(path); 2996 if (app_bundle_path.empty()) { 2997 DNBLogThreadedIf( 2998 LOG_PROCESS, 2999 "MachProcess::PrepareForAttach(): path '%s' doesn't contain .app, " 3000 "we can't tell springboard to wait for launch...", 3001 path); 3002 return NULL; 3003 } 3004 3005#if defined(WITH_FBS) 3006 if (launch_flavor == eLaunchFlavorDefault) 3007 launch_flavor = eLaunchFlavorFBS; 3008 if (launch_flavor != eLaunchFlavorFBS) 3009 return NULL; 3010#elif defined(WITH_BKS) 3011 if (launch_flavor == eLaunchFlavorDefault) 3012 launch_flavor = eLaunchFlavorBKS; 3013 if (launch_flavor != eLaunchFlavorBKS) 3014 return NULL; 3015#elif defined(WITH_SPRINGBOARD) 3016 if (launch_flavor == eLaunchFlavorDefault) 3017 launch_flavor = eLaunchFlavorSpringBoard; 3018 if (launch_flavor != eLaunchFlavorSpringBoard) 3019 return NULL; 3020#endif 3021 3022 CFStringRef bundleIDCFStr = 3023 CopyBundleIDForPath(app_bundle_path.c_str(), attach_err); 3024 std::string bundleIDStr; 3025 CFString::UTF8(bundleIDCFStr, bundleIDStr); 3026 DNBLogThreadedIf(LOG_PROCESS, 3027 "CopyBundleIDForPath (%s, err_str) returned @\"%s\"", 3028 app_bundle_path.c_str(), bundleIDStr.c_str()); 3029 3030 if (bundleIDCFStr == NULL) { 3031 return NULL; 3032 } 3033 3034#if defined(WITH_FBS) 3035 if (launch_flavor == eLaunchFlavorFBS) { 3036 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 3037 3038 NSString *stdio_path = nil; 3039 NSFileManager *file_manager = [NSFileManager defaultManager]; 3040 const char *null_path = "/dev/null"; 3041 stdio_path = 3042 [file_manager stringWithFileSystemRepresentation:null_path 3043 length:strlen(null_path)]; 3044 3045 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 3046 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 3047 3048 DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: " 3049 "@\"%s\",options include stdio path: \"%s\", " 3050 "BKSDebugOptionKeyDebugOnNextLaunch & " 3051 "BKSDebugOptionKeyWaitForDebugger )", 3052 bundleIDStr.c_str(), null_path); 3053 3054 [debug_options setObject:stdio_path 3055 forKey:FBSDebugOptionKeyStandardOutPath]; 3056 [debug_options setObject:stdio_path 3057 forKey:FBSDebugOptionKeyStandardErrorPath]; 3058 [debug_options setObject:[NSNumber numberWithBool:YES] 3059 forKey:FBSDebugOptionKeyWaitForDebugger]; 3060 [debug_options setObject:[NSNumber numberWithBool:YES] 3061 forKey:FBSDebugOptionKeyDebugOnNextLaunch]; 3062 3063 [options setObject:debug_options 3064 forKey:FBSOpenApplicationOptionKeyDebuggingOptions]; 3065 3066 FBSSystemService *system_service = [[FBSSystemService alloc] init]; 3067 3068 mach_port_t client_port = [system_service createClientPort]; 3069 __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 3070 __block FBSOpenApplicationErrorCode attach_error_code = 3071 FBSOpenApplicationErrorCodeNone; 3072 3073 NSString *bundleIDNSStr = (NSString *)bundleIDCFStr; 3074 3075 DNBLog("[LaunchAttach] START (%d) requesting FBS launch of app with bundle " 3076 "ID '%s'", 3077 getpid(), bundleIDStr.c_str()); 3078 [system_service openApplication:bundleIDNSStr 3079 options:options 3080 clientPort:client_port 3081 withResult:^(NSError *error) { 3082 // The system service will cleanup the client port we 3083 // created for us. 3084 if (error) 3085 attach_error_code = 3086 (FBSOpenApplicationErrorCode)[error code]; 3087 3088 [system_service release]; 3089 dispatch_semaphore_signal(semaphore); 3090 }]; 3091 3092 const uint32_t timeout_secs = 9; 3093 3094 dispatch_time_t timeout = 3095 dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); 3096 3097 long success = dispatch_semaphore_wait(semaphore, timeout) == 0; 3098 3099 if (!success) { 3100 DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str()); 3101 attach_err.SetErrorString( 3102 "debugserver timed out waiting for openApplication to complete."); 3103 attach_err.SetError(OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); 3104 } else if (attach_error_code != FBSOpenApplicationErrorCodeNone) { 3105 std::string empty_str; 3106 SetFBSError(attach_error_code, empty_str, attach_err); 3107 DNBLogError("unable to launch the application with CFBundleIdentifier " 3108 "'%s' bks_error = %ld", 3109 bundleIDStr.c_str(), (NSInteger)attach_error_code); 3110 } 3111 dispatch_release(semaphore); 3112 [pool drain]; 3113 } 3114#endif 3115#if defined(WITH_BKS) 3116 if (launch_flavor == eLaunchFlavorBKS) { 3117 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 3118 3119 NSString *stdio_path = nil; 3120 NSFileManager *file_manager = [NSFileManager defaultManager]; 3121 const char *null_path = "/dev/null"; 3122 stdio_path = 3123 [file_manager stringWithFileSystemRepresentation:null_path 3124 length:strlen(null_path)]; 3125 3126 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 3127 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 3128 3129 DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: " 3130 "@\"%s\",options include stdio path: \"%s\", " 3131 "BKSDebugOptionKeyDebugOnNextLaunch & " 3132 "BKSDebugOptionKeyWaitForDebugger )", 3133 bundleIDStr.c_str(), null_path); 3134 3135 [debug_options setObject:stdio_path 3136 forKey:BKSDebugOptionKeyStandardOutPath]; 3137 [debug_options setObject:stdio_path 3138 forKey:BKSDebugOptionKeyStandardErrorPath]; 3139 [debug_options setObject:[NSNumber numberWithBool:YES] 3140 forKey:BKSDebugOptionKeyWaitForDebugger]; 3141 [debug_options setObject:[NSNumber numberWithBool:YES] 3142 forKey:BKSDebugOptionKeyDebugOnNextLaunch]; 3143 3144 [options setObject:debug_options 3145 forKey:BKSOpenApplicationOptionKeyDebuggingOptions]; 3146 3147 BKSSystemService *system_service = [[BKSSystemService alloc] init]; 3148 3149 mach_port_t client_port = [system_service createClientPort]; 3150 __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 3151 __block BKSOpenApplicationErrorCode attach_error_code = 3152 BKSOpenApplicationErrorCodeNone; 3153 3154 NSString *bundleIDNSStr = (NSString *)bundleIDCFStr; 3155 3156 DNBLog("[LaunchAttach] START (%d) requesting BKS launch of app with bundle " 3157 "ID '%s'", 3158 getpid(), bundleIDStr.c_str()); 3159 [system_service openApplication:bundleIDNSStr 3160 options:options 3161 clientPort:client_port 3162 withResult:^(NSError *error) { 3163 // The system service will cleanup the client port we 3164 // created for us. 3165 if (error) 3166 attach_error_code = 3167 (BKSOpenApplicationErrorCode)[error code]; 3168 3169 [system_service release]; 3170 dispatch_semaphore_signal(semaphore); 3171 }]; 3172 3173 const uint32_t timeout_secs = 9; 3174 3175 dispatch_time_t timeout = 3176 dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); 3177 3178 long success = dispatch_semaphore_wait(semaphore, timeout) == 0; 3179 3180 if (!success) { 3181 DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str()); 3182 attach_err.SetErrorString( 3183 "debugserver timed out waiting for openApplication to complete."); 3184 attach_err.SetError(OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); 3185 } else if (attach_error_code != BKSOpenApplicationErrorCodeNone) { 3186 std::string empty_str; 3187 SetBKSError(attach_error_code, empty_str, attach_err); 3188 DNBLogError("unable to launch the application with CFBundleIdentifier " 3189 "'%s' bks_error = %d", 3190 bundleIDStr.c_str(), attach_error_code); 3191 } 3192 dispatch_release(semaphore); 3193 [pool drain]; 3194 } 3195#endif 3196 3197#if defined(WITH_SPRINGBOARD) 3198 if (launch_flavor == eLaunchFlavorSpringBoard) { 3199 SBSApplicationLaunchError sbs_error = 0; 3200 3201 const char *stdout_err = "/dev/null"; 3202 CFString stdio_path; 3203 stdio_path.SetFileSystemRepresentation(stdout_err); 3204 3205 DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" " 3206 ", NULL, NULL, NULL, @\"%s\", @\"%s\", " 3207 "SBSApplicationDebugOnNextLaunch | " 3208 "SBSApplicationLaunchWaitForDebugger )", 3209 bundleIDStr.c_str(), stdout_err, stdout_err); 3210 3211 DNBLog("[LaunchAttach] START (%d) requesting SpringBoard launch of app " 3212 "with bundle " 3213 "ID '%s'", 3214 getpid(), bundleIDStr.c_str()); 3215 sbs_error = SBSLaunchApplicationForDebugging( 3216 bundleIDCFStr, 3217 (CFURLRef)NULL, // openURL 3218 NULL, // launch_argv.get(), 3219 NULL, // launch_envp.get(), // CFDictionaryRef environment 3220 stdio_path.get(), stdio_path.get(), 3221 SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger); 3222 3223 if (sbs_error != SBSApplicationLaunchErrorSuccess) { 3224 attach_err.SetError(sbs_error, DNBError::SpringBoard); 3225 return NULL; 3226 } 3227 } 3228#endif // WITH_SPRINGBOARD 3229 3230 DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch."); 3231 return bundleIDCFStr; 3232#else // !(defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined 3233 // (WITH_FBS)) 3234 return NULL; 3235#endif 3236} 3237 3238// Pass in the token you got from PrepareForAttach. If there is a process 3239// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS 3240// will be returned. 3241 3242nub_process_t MachProcess::CheckForProcess(const void *attach_token, 3243 nub_launch_flavor_t launch_flavor) { 3244 if (attach_token == NULL) 3245 return INVALID_NUB_PROCESS; 3246 3247#if defined(WITH_FBS) 3248 if (launch_flavor == eLaunchFlavorFBS) { 3249 NSString *bundleIDNSStr = (NSString *)attach_token; 3250 FBSSystemService *systemService = [[FBSSystemService alloc] init]; 3251 pid_t pid = [systemService pidForApplication:bundleIDNSStr]; 3252 [systemService release]; 3253 if (pid == 0) 3254 return INVALID_NUB_PROCESS; 3255 else 3256 return pid; 3257 } 3258#endif 3259 3260#if defined(WITH_BKS) 3261 if (launch_flavor == eLaunchFlavorBKS) { 3262 NSString *bundleIDNSStr = (NSString *)attach_token; 3263 BKSSystemService *systemService = [[BKSSystemService alloc] init]; 3264 pid_t pid = [systemService pidForApplication:bundleIDNSStr]; 3265 [systemService release]; 3266 if (pid == 0) 3267 return INVALID_NUB_PROCESS; 3268 else 3269 return pid; 3270 } 3271#endif 3272 3273#if defined(WITH_SPRINGBOARD) 3274 if (launch_flavor == eLaunchFlavorSpringBoard) { 3275 CFStringRef bundleIDCFStr = (CFStringRef)attach_token; 3276 Boolean got_it; 3277 nub_process_t attach_pid; 3278 got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid); 3279 if (got_it) 3280 return attach_pid; 3281 else 3282 return INVALID_NUB_PROCESS; 3283 } 3284#endif 3285 return INVALID_NUB_PROCESS; 3286} 3287 3288// Call this to clean up after you have either attached or given up on the 3289// attach. 3290// Pass true for success if you have attached, false if you have not. 3291// The token will also be freed at this point, so you can't use it after calling 3292// this method. 3293 3294void MachProcess::CleanupAfterAttach(const void *attach_token, 3295 nub_launch_flavor_t launch_flavor, 3296 bool success, DNBError &err_str) { 3297 if (attach_token == NULL) 3298 return; 3299 3300#if defined(WITH_FBS) 3301 if (launch_flavor == eLaunchFlavorFBS) { 3302 if (!success) { 3303 FBSCleanupAfterAttach(attach_token, err_str); 3304 } 3305 CFRelease((CFStringRef)attach_token); 3306 } 3307#endif 3308 3309#if defined(WITH_BKS) 3310 3311 if (launch_flavor == eLaunchFlavorBKS) { 3312 if (!success) { 3313 BKSCleanupAfterAttach(attach_token, err_str); 3314 } 3315 CFRelease((CFStringRef)attach_token); 3316 } 3317#endif 3318 3319#if defined(WITH_SPRINGBOARD) 3320 // Tell SpringBoard to cancel the debug on next launch of this application 3321 // if we failed to attach 3322 if (launch_flavor == eMachProcessFlagsUsingSpringBoard) { 3323 if (!success) { 3324 SBSApplicationLaunchError sbs_error = 0; 3325 CFStringRef bundleIDCFStr = (CFStringRef)attach_token; 3326 3327 sbs_error = SBSLaunchApplicationForDebugging( 3328 bundleIDCFStr, (CFURLRef)NULL, NULL, NULL, NULL, NULL, 3329 SBSApplicationCancelDebugOnNextLaunch); 3330 3331 if (sbs_error != SBSApplicationLaunchErrorSuccess) { 3332 err_str.SetError(sbs_error, DNBError::SpringBoard); 3333 return; 3334 } 3335 } 3336 3337 CFRelease((CFStringRef)attach_token); 3338 } 3339#endif 3340} 3341 3342pid_t MachProcess::LaunchForDebug( 3343 const char *path, char const *argv[], char const *envp[], 3344 const char *working_directory, // NULL => don't change, non-NULL => set 3345 // working directory for inferior to this 3346 const char *stdin_path, const char *stdout_path, const char *stderr_path, 3347 bool no_stdio, nub_launch_flavor_t launch_flavor, int disable_aslr, 3348 const char *event_data, 3349 const RNBContext::IgnoredExceptions &ignored_exceptions, 3350 DNBError &launch_err) { 3351 // Clear out and clean up from any current state 3352 Clear(); 3353 3354 DNBLogThreadedIf(LOG_PROCESS, 3355 "%s( path = '%s', argv = %p, envp = %p, " 3356 "launch_flavor = %u, disable_aslr = %d )", 3357 __FUNCTION__, path, static_cast<const void *>(argv), 3358 static_cast<const void *>(envp), launch_flavor, 3359 disable_aslr); 3360 3361 // Fork a child process for debugging 3362 SetState(eStateLaunching); 3363 3364 switch (launch_flavor) { 3365 case eLaunchFlavorForkExec: 3366 m_pid = MachProcess::ForkChildForPTraceDebugging(path, argv, envp, this, 3367 launch_err); 3368 break; 3369#ifdef WITH_FBS 3370 case eLaunchFlavorFBS: { 3371 std::string app_bundle_path = GetAppBundle(path); 3372 if (!app_bundle_path.empty()) { 3373 m_flags |= (eMachProcessFlagsUsingFBS | eMachProcessFlagsBoardCalculated); 3374 if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp, 3375 no_stdio, disable_aslr, event_data, 3376 ignored_exceptions, launch_err) != 0) 3377 return m_pid; // A successful SBLaunchForDebug() returns and assigns a 3378 // non-zero m_pid. 3379 } 3380 DNBLog("Failed to launch '%s' with FBS", app_bundle_path.c_str()); 3381 } break; 3382#endif 3383#ifdef WITH_BKS 3384 case eLaunchFlavorBKS: { 3385 std::string app_bundle_path = GetAppBundle(path); 3386 if (!app_bundle_path.empty()) { 3387 m_flags |= (eMachProcessFlagsUsingBKS | eMachProcessFlagsBoardCalculated); 3388 if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp, 3389 no_stdio, disable_aslr, event_data, 3390 ignored_exceptions, launch_err) != 0) 3391 return m_pid; // A successful SBLaunchForDebug() returns and assigns a 3392 // non-zero m_pid. 3393 } 3394 DNBLog("Failed to launch '%s' with BKS", app_bundle_path.c_str()); 3395 } break; 3396#endif 3397#ifdef WITH_SPRINGBOARD 3398 case eLaunchFlavorSpringBoard: { 3399 std::string app_bundle_path = GetAppBundle(path); 3400 if (!app_bundle_path.empty()) { 3401 if (SBLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio, 3402 disable_aslr, ignored_exceptions, launch_err) != 0) 3403 return m_pid; // A successful SBLaunchForDebug() returns and assigns a 3404 // non-zero m_pid. 3405 } 3406 DNBLog("Failed to launch '%s' with SpringBoard", app_bundle_path.c_str()); 3407 } break; 3408 3409#endif 3410 3411 case eLaunchFlavorPosixSpawn: 3412 m_pid = MachProcess::PosixSpawnChildForPTraceDebugging( 3413 path, DNBArchProtocol::GetCPUType(), DNBArchProtocol::GetCPUSubType(), 3414 argv, envp, working_directory, stdin_path, stdout_path, stderr_path, 3415 no_stdio, this, disable_aslr, launch_err); 3416 break; 3417 3418 default: 3419 DNBLog("Failed to launch: invalid launch flavor: %d", launch_flavor); 3420 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 3421 return INVALID_NUB_PROCESS; 3422 } 3423 3424 if (m_pid == INVALID_NUB_PROCESS) { 3425 // If we don't have a valid process ID and no one has set the error, 3426 // then return a generic error 3427 if (launch_err.Success()) 3428 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 3429 } else { 3430 m_path = path; 3431 size_t i; 3432 char const *arg; 3433 for (i = 0; (arg = argv[i]) != NULL; i++) 3434 m_args.push_back(arg); 3435 3436 m_task.StartExceptionThread(ignored_exceptions, launch_err); 3437 if (launch_err.Fail()) { 3438 if (launch_err.AsString() == NULL) 3439 launch_err.SetErrorString("unable to start the exception thread"); 3440 DNBLog("Could not get inferior's Mach exception port, sending ptrace " 3441 "PT_KILL and exiting."); 3442 ::ptrace(PT_KILL, m_pid, 0, 0); 3443 m_pid = INVALID_NUB_PROCESS; 3444 return INVALID_NUB_PROCESS; 3445 } 3446 3447 StartSTDIOThread(); 3448 3449 if (launch_flavor == eLaunchFlavorPosixSpawn) { 3450 3451 SetState(eStateAttaching); 3452 errno = 0; 3453 DNBLog("[LaunchAttach] (%d) About to ptrace(PT_ATTACHEXC, %d)...", 3454 getpid(), m_pid); 3455 int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0); 3456 int ptrace_errno = errno; 3457 DNBLog("[LaunchAttach] (%d) Completed ptrace(PT_ATTACHEXC, %d) == %d", 3458 getpid(), m_pid, err); 3459 if (err == 0) { 3460 m_flags |= eMachProcessFlagsAttached; 3461 DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid); 3462 launch_err.Clear(); 3463 } else { 3464 SetState(eStateExited); 3465 DNBError ptrace_err(ptrace_errno, DNBError::POSIX); 3466 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to spawned pid " 3467 "%d (err = %i, errno = %i (%s))", 3468 m_pid, err, ptrace_err.Status(), 3469 ptrace_err.AsString()); 3470 char err_msg[PATH_MAX]; 3471 3472 snprintf(err_msg, sizeof(err_msg), 3473 "Failed to attach to pid %d, LaunchForDebug() unable to " 3474 "ptrace(PT_ATTACHEXC)", 3475 m_pid); 3476 launch_err.SetErrorString(err_msg); 3477 } 3478 } else { 3479 launch_err.Clear(); 3480 } 3481 } 3482 return m_pid; 3483} 3484 3485pid_t MachProcess::PosixSpawnChildForPTraceDebugging( 3486 const char *path, cpu_type_t cpu_type, cpu_subtype_t cpu_subtype, 3487 char const *argv[], char const *envp[], const char *working_directory, 3488 const char *stdin_path, const char *stdout_path, const char *stderr_path, 3489 bool no_stdio, MachProcess *process, int disable_aslr, DNBError &err) { 3490 posix_spawnattr_t attr; 3491 short flags; 3492 DNBLogThreadedIf(LOG_PROCESS, 3493 "%s ( path='%s', argv=%p, envp=%p, " 3494 "working_dir=%s, stdin=%s, stdout=%s " 3495 "stderr=%s, no-stdio=%i)", 3496 __FUNCTION__, path, static_cast<const void *>(argv), 3497 static_cast<const void *>(envp), working_directory, 3498 stdin_path, stdout_path, stderr_path, no_stdio); 3499 3500 err.SetError(::posix_spawnattr_init(&attr), DNBError::POSIX); 3501 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 3502 err.LogThreaded("::posix_spawnattr_init ( &attr )"); 3503 if (err.Fail()) 3504 return INVALID_NUB_PROCESS; 3505 3506 flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF | 3507 POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETPGROUP; 3508 if (disable_aslr) 3509 flags |= _POSIX_SPAWN_DISABLE_ASLR; 3510 3511 sigset_t no_signals; 3512 sigset_t all_signals; 3513 sigemptyset(&no_signals); 3514 sigfillset(&all_signals); 3515 ::posix_spawnattr_setsigmask(&attr, &no_signals); 3516 ::posix_spawnattr_setsigdefault(&attr, &all_signals); 3517 3518 err.SetError(::posix_spawnattr_setflags(&attr, flags), DNBError::POSIX); 3519 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 3520 err.LogThreaded( 3521 "::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )", 3522 flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR" 3523 : ""); 3524 if (err.Fail()) 3525 return INVALID_NUB_PROCESS; 3526 3527// Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail 3528// and we will fail to continue with our process... 3529 3530// On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment.... 3531 3532 if (cpu_type != 0) { 3533 size_t ocount = 0; 3534 bool slice_preference_set = false; 3535 3536 if (cpu_subtype != 0) { 3537 typedef int (*posix_spawnattr_setarchpref_np_t)( 3538 posix_spawnattr_t *, size_t, cpu_type_t *, cpu_subtype_t *, size_t *); 3539 posix_spawnattr_setarchpref_np_t posix_spawnattr_setarchpref_np_fn = 3540 (posix_spawnattr_setarchpref_np_t)dlsym( 3541 RTLD_DEFAULT, "posix_spawnattr_setarchpref_np"); 3542 if (posix_spawnattr_setarchpref_np_fn) { 3543 err.SetError((*posix_spawnattr_setarchpref_np_fn)( 3544 &attr, 1, &cpu_type, &cpu_subtype, &ocount)); 3545 slice_preference_set = err.Success(); 3546 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 3547 err.LogThreaded( 3548 "::posix_spawnattr_setarchpref_np ( &attr, 1, cpu_type = " 3549 "0x%8.8x, cpu_subtype = 0x%8.8x, count => %llu )", 3550 cpu_type, cpu_subtype, (uint64_t)ocount); 3551 if (err.Fail() != 0 || ocount != 1) 3552 return INVALID_NUB_PROCESS; 3553 } 3554 } 3555 3556 if (!slice_preference_set) { 3557 err.SetError( 3558 ::posix_spawnattr_setbinpref_np(&attr, 1, &cpu_type, &ocount), 3559 DNBError::POSIX); 3560 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 3561 err.LogThreaded( 3562 "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = " 3563 "0x%8.8x, count => %llu )", 3564 cpu_type, (uint64_t)ocount); 3565 3566 if (err.Fail() != 0 || ocount != 1) 3567 return INVALID_NUB_PROCESS; 3568 } 3569 } 3570 3571 PseudoTerminal pty; 3572 3573 posix_spawn_file_actions_t file_actions; 3574 err.SetError(::posix_spawn_file_actions_init(&file_actions), DNBError::POSIX); 3575 int file_actions_valid = err.Success(); 3576 if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS)) 3577 err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )"); 3578 int pty_error = -1; 3579 pid_t pid = INVALID_NUB_PROCESS; 3580 if (file_actions_valid) { 3581 if (stdin_path == NULL && stdout_path == NULL && stderr_path == NULL && 3582 !no_stdio) { 3583 pty_error = pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY); 3584 if (pty_error == PseudoTerminal::success) { 3585 stdin_path = stdout_path = stderr_path = pty.SecondaryName(); 3586 } 3587 } 3588 3589 // if no_stdio or std paths not supplied, then route to "/dev/null". 3590 if (no_stdio || stdin_path == NULL || stdin_path[0] == '\0') 3591 stdin_path = "/dev/null"; 3592 if (no_stdio || stdout_path == NULL || stdout_path[0] == '\0') 3593 stdout_path = "/dev/null"; 3594 if (no_stdio || stderr_path == NULL || stderr_path[0] == '\0') 3595 stderr_path = "/dev/null"; 3596 3597 err.SetError(::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, 3598 stdin_path, 3599 O_RDONLY | O_NOCTTY, 0), 3600 DNBError::POSIX); 3601 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 3602 err.LogThreaded("::posix_spawn_file_actions_addopen (&file_actions, " 3603 "filedes=STDIN_FILENO, path='%s')", 3604 stdin_path); 3605 3606 err.SetError(::posix_spawn_file_actions_addopen( 3607 &file_actions, STDOUT_FILENO, stdout_path, 3608 O_WRONLY | O_NOCTTY | O_CREAT, 0640), 3609 DNBError::POSIX); 3610 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 3611 err.LogThreaded("::posix_spawn_file_actions_addopen (&file_actions, " 3612 "filedes=STDOUT_FILENO, path='%s')", 3613 stdout_path); 3614 3615 err.SetError(::posix_spawn_file_actions_addopen( 3616 &file_actions, STDERR_FILENO, stderr_path, 3617 O_WRONLY | O_NOCTTY | O_CREAT, 0640), 3618 DNBError::POSIX); 3619 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 3620 err.LogThreaded("::posix_spawn_file_actions_addopen (&file_actions, " 3621 "filedes=STDERR_FILENO, path='%s')", 3622 stderr_path); 3623 3624 // TODO: Verify if we can set the working directory back immediately 3625 // after the posix_spawnp call without creating a race condition??? 3626 if (working_directory) 3627 ::chdir(working_directory); 3628 3629 err.SetError(::posix_spawnp(&pid, path, &file_actions, &attr, 3630 const_cast<char *const *>(argv), 3631 const_cast<char *const *>(envp)), 3632 DNBError::POSIX); 3633 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 3634 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = " 3635 "%p, attr = %p, argv = %p, envp = %p )", 3636 pid, path, &file_actions, &attr, argv, envp); 3637 } else { 3638 // TODO: Verify if we can set the working directory back immediately 3639 // after the posix_spawnp call without creating a race condition??? 3640 if (working_directory) 3641 ::chdir(working_directory); 3642 3643 err.SetError(::posix_spawnp(&pid, path, NULL, &attr, 3644 const_cast<char *const *>(argv), 3645 const_cast<char *const *>(envp)), 3646 DNBError::POSIX); 3647 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 3648 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = " 3649 "%p, attr = %p, argv = %p, envp = %p )", 3650 pid, path, NULL, &attr, argv, envp); 3651 } 3652 3653 // We have seen some cases where posix_spawnp was returning a valid 3654 // looking pid even when an error was returned, so clear it out 3655 if (err.Fail()) 3656 pid = INVALID_NUB_PROCESS; 3657 3658 if (pty_error == 0) { 3659 if (process != NULL) { 3660 int primary_fd = pty.ReleasePrimaryFD(); 3661 process->SetChildFileDescriptors(primary_fd, primary_fd, primary_fd); 3662 } 3663 } 3664 ::posix_spawnattr_destroy(&attr); 3665 3666 if (pid != INVALID_NUB_PROCESS) { 3667 cpu_type_t pid_cpu_type = MachProcess::GetCPUTypeForLocalProcess(pid); 3668 DNBLogThreadedIf(LOG_PROCESS, 3669 "MachProcess::%s ( ) pid=%i, cpu_type=0x%8.8x", 3670 __FUNCTION__, pid, pid_cpu_type); 3671 if (pid_cpu_type) 3672 DNBArchProtocol::SetArchitecture(pid_cpu_type); 3673 } 3674 3675 if (file_actions_valid) { 3676 DNBError err2; 3677 err2.SetError(::posix_spawn_file_actions_destroy(&file_actions), 3678 DNBError::POSIX); 3679 if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 3680 err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )"); 3681 } 3682 3683 return pid; 3684} 3685 3686uint32_t MachProcess::GetCPUTypeForLocalProcess(pid_t pid) { 3687 int mib[CTL_MAXNAME] = { 3688 0, 3689 }; 3690 size_t len = CTL_MAXNAME; 3691 if (::sysctlnametomib("sysctl.proc_cputype", mib, &len)) 3692 return 0; 3693 3694 mib[len] = pid; 3695 len++; 3696 3697 cpu_type_t cpu; 3698 size_t cpu_len = sizeof(cpu); 3699 if (::sysctl(mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0)) 3700 cpu = 0; 3701 return cpu; 3702} 3703 3704pid_t MachProcess::ForkChildForPTraceDebugging(const char *path, 3705 char const *argv[], 3706 char const *envp[], 3707 MachProcess *process, 3708 DNBError &launch_err) { 3709 PseudoTerminal::Status pty_error = PseudoTerminal::success; 3710 3711 // Use a fork that ties the child process's stdin/out/err to a pseudo 3712 // terminal so we can read it in our MachProcess::STDIOThread 3713 // as unbuffered io. 3714 PseudoTerminal pty; 3715 pid_t pid = pty.Fork(pty_error); 3716 3717 if (pid < 0) { 3718 //-------------------------------------------------------------- 3719 // Status during fork. 3720 //-------------------------------------------------------------- 3721 return pid; 3722 } else if (pid == 0) { 3723 //-------------------------------------------------------------- 3724 // Child process 3725 //-------------------------------------------------------------- 3726 ::ptrace(PT_TRACE_ME, 0, 0, 0); // Debug this process 3727 ::ptrace(PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions 3728 3729 // If our parent is setgid, lets make sure we don't inherit those 3730 // extra powers due to nepotism. 3731 if (::setgid(getgid()) == 0) { 3732 3733 // Let the child have its own process group. We need to execute 3734 // this call in both the child and parent to avoid a race condition 3735 // between the two processes. 3736 ::setpgid(0, 0); // Set the child process group to match its pid 3737 3738 // Sleep a bit to before the exec call 3739 ::sleep(1); 3740 3741 // Turn this process into 3742 ::execv(path, const_cast<char *const *>(argv)); 3743 } 3744 // Exit with error code. Child process should have taken 3745 // over in above exec call and if the exec fails it will 3746 // exit the child process below. 3747 ::exit(127); 3748 } else { 3749 //-------------------------------------------------------------- 3750 // Parent process 3751 //-------------------------------------------------------------- 3752 // Let the child have its own process group. We need to execute 3753 // this call in both the child and parent to avoid a race condition 3754 // between the two processes. 3755 ::setpgid(pid, pid); // Set the child process group to match its pid 3756 3757 if (process != NULL) { 3758 // Release our primary pty file descriptor so the pty class doesn't 3759 // close it and so we can continue to use it in our STDIO thread 3760 int primary_fd = pty.ReleasePrimaryFD(); 3761 process->SetChildFileDescriptors(primary_fd, primary_fd, primary_fd); 3762 } 3763 } 3764 return pid; 3765} 3766 3767#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) || defined(WITH_FBS) 3768// This returns a CFRetained pointer to the Bundle ID for app_bundle_path, 3769// or NULL if there was some problem getting the bundle id. 3770static CFStringRef CopyBundleIDForPath(const char *app_bundle_path, 3771 DNBError &err_str) { 3772 CFBundle bundle(app_bundle_path); 3773 CFStringRef bundleIDCFStr = bundle.GetIdentifier(); 3774 std::string bundleID; 3775 if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) { 3776 struct stat app_bundle_stat; 3777 char err_msg[PATH_MAX]; 3778 3779 if (::stat(app_bundle_path, &app_bundle_stat) < 0) { 3780 err_str.SetError(errno, DNBError::POSIX); 3781 snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), 3782 app_bundle_path); 3783 err_str.SetErrorString(err_msg); 3784 DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg); 3785 } else { 3786 err_str.SetError(-1, DNBError::Generic); 3787 snprintf(err_msg, sizeof(err_msg), 3788 "failed to extract CFBundleIdentifier from %s", app_bundle_path); 3789 err_str.SetErrorString(err_msg); 3790 DNBLogThreadedIf( 3791 LOG_PROCESS, 3792 "%s() error: failed to extract CFBundleIdentifier from '%s'", 3793 __FUNCTION__, app_bundle_path); 3794 } 3795 return NULL; 3796 } 3797 3798 DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", 3799 __FUNCTION__, bundleID.c_str()); 3800 CFRetain(bundleIDCFStr); 3801 3802 return bundleIDCFStr; 3803} 3804#endif // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined 3805 // (WITH_FBS) 3806#ifdef WITH_SPRINGBOARD 3807 3808pid_t MachProcess::SBLaunchForDebug(const char *path, char const *argv[], 3809 char const *envp[], bool no_stdio, 3810 bool disable_aslr, 3811 const RNBContext::IgnoredExceptions 3812 &ignored_exceptions, 3813 DNBError &launch_err) { 3814 // Clear out and clean up from any current state 3815 Clear(); 3816 3817 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); 3818 3819 // Fork a child process for debugging 3820 SetState(eStateLaunching); 3821 m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, no_stdio, 3822 this, launch_err); 3823 if (m_pid != 0) { 3824 m_path = path; 3825 size_t i; 3826 char const *arg; 3827 for (i = 0; (arg = argv[i]) != NULL; i++) 3828 m_args.push_back(arg); 3829 m_task.StartExceptionThread(ignored_exceptions, launch_err); 3830 3831 if (launch_err.Fail()) { 3832 if (launch_err.AsString() == NULL) 3833 launch_err.SetErrorString("unable to start the exception thread"); 3834 DNBLog("Could not get inferior's Mach exception port, sending ptrace " 3835 "PT_KILL and exiting."); 3836 ::ptrace(PT_KILL, m_pid, 0, 0); 3837 m_pid = INVALID_NUB_PROCESS; 3838 return INVALID_NUB_PROCESS; 3839 } 3840 3841 StartSTDIOThread(); 3842 SetState(eStateAttaching); 3843 DNBLog("[LaunchAttach] (%d) About to ptrace(PT_ATTACHEXC, %d)...", getpid(), 3844 m_pid); 3845 int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0); 3846 DNBLog("[LaunchAttach] (%d) Completed ptrace(PT_ATTACHEXC, %d) == %d", 3847 getpid(), m_pid, err); 3848 if (err == 0) { 3849 m_flags |= eMachProcessFlagsAttached; 3850 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid); 3851 } else { 3852 launch_err.SetErrorString( 3853 "Failed to attach to pid %d, SBLaunchForDebug() unable to " 3854 "ptrace(PT_ATTACHEXC)", 3855 m_pid); 3856 SetState(eStateExited); 3857 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid); 3858 } 3859 } 3860 return m_pid; 3861} 3862 3863#include <servers/bootstrap.h> 3864 3865pid_t MachProcess::SBForkChildForPTraceDebugging( 3866 const char *app_bundle_path, char const *argv[], char const *envp[], 3867 bool no_stdio, MachProcess *process, DNBError &launch_err) { 3868 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, 3869 app_bundle_path, process); 3870 CFAllocatorRef alloc = kCFAllocatorDefault; 3871 3872 if (argv[0] == NULL) 3873 return INVALID_NUB_PROCESS; 3874 3875 size_t argc = 0; 3876 // Count the number of arguments 3877 while (argv[argc] != NULL) 3878 argc++; 3879 3880 // Enumerate the arguments 3881 size_t first_launch_arg_idx = 1; 3882 CFReleaser<CFMutableArrayRef> launch_argv; 3883 3884 if (argv[first_launch_arg_idx]) { 3885 size_t launch_argc = argc > 0 ? argc - 1 : 0; 3886 launch_argv.reset( 3887 ::CFArrayCreateMutable(alloc, launch_argc, &kCFTypeArrayCallBacks)); 3888 size_t i; 3889 char const *arg; 3890 CFString launch_arg; 3891 for (i = first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); 3892 i++) { 3893 launch_arg.reset( 3894 ::CFStringCreateWithCString(alloc, arg, kCFStringEncodingUTF8)); 3895 if (launch_arg.get() != NULL) 3896 CFArrayAppendValue(launch_argv.get(), launch_arg.get()); 3897 else 3898 break; 3899 } 3900 } 3901 3902 // Next fill in the arguments dictionary. Note, the envp array is of the form 3903 // Variable=value but SpringBoard wants a CF dictionary. So we have to 3904 // convert 3905 // this here. 3906 3907 CFReleaser<CFMutableDictionaryRef> launch_envp; 3908 3909 if (envp[0]) { 3910 launch_envp.reset( 3911 ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, 3912 &kCFTypeDictionaryValueCallBacks)); 3913 const char *value; 3914 int name_len; 3915 CFString name_string, value_string; 3916 3917 for (int i = 0; envp[i] != NULL; i++) { 3918 value = strstr(envp[i], "="); 3919 3920 // If the name field is empty or there's no =, skip it. Somebody's 3921 // messing with us. 3922 if (value == NULL || value == envp[i]) 3923 continue; 3924 3925 name_len = value - envp[i]; 3926 3927 // Now move value over the "=" 3928 value++; 3929 3930 name_string.reset( 3931 ::CFStringCreateWithBytes(alloc, (const UInt8 *)envp[i], name_len, 3932 kCFStringEncodingUTF8, false)); 3933 value_string.reset( 3934 ::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8)); 3935 CFDictionarySetValue(launch_envp.get(), name_string.get(), 3936 value_string.get()); 3937 } 3938 } 3939 3940 CFString stdio_path; 3941 3942 PseudoTerminal pty; 3943 if (!no_stdio) { 3944 PseudoTerminal::Status pty_err = 3945 pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY); 3946 if (pty_err == PseudoTerminal::success) { 3947 const char *secondary_name = pty.SecondaryName(); 3948 DNBLogThreadedIf(LOG_PROCESS, 3949 "%s() successfully opened primary pty, secondary is %s", 3950 __FUNCTION__, secondary_name); 3951 if (secondary_name && secondary_name[0]) { 3952 ::chmod(secondary_name, S_IRWXU | S_IRWXG | S_IRWXO); 3953 stdio_path.SetFileSystemRepresentation(secondary_name); 3954 } 3955 } 3956 } 3957 3958 if (stdio_path.get() == NULL) { 3959 stdio_path.SetFileSystemRepresentation("/dev/null"); 3960 } 3961 3962 CFStringRef bundleIDCFStr = CopyBundleIDForPath(app_bundle_path, launch_err); 3963 if (bundleIDCFStr == NULL) 3964 return INVALID_NUB_PROCESS; 3965 3966 // This is just for logging: 3967 std::string bundleID; 3968 CFString::UTF8(bundleIDCFStr, bundleID); 3969 3970 DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", 3971 __FUNCTION__); 3972 3973 // Find SpringBoard 3974 SBSApplicationLaunchError sbs_error = 0; 3975 sbs_error = SBSLaunchApplicationForDebugging( 3976 bundleIDCFStr, 3977 (CFURLRef)NULL, // openURL 3978 launch_argv.get(), 3979 launch_envp.get(), // CFDictionaryRef environment 3980 stdio_path.get(), stdio_path.get(), 3981 SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice); 3982 3983 launch_err.SetError(sbs_error, DNBError::SpringBoard); 3984 3985 if (sbs_error == SBSApplicationLaunchErrorSuccess) { 3986 static const useconds_t pid_poll_interval = 200000; 3987 static const useconds_t pid_poll_timeout = 30000000; 3988 3989 useconds_t pid_poll_total = 0; 3990 3991 nub_process_t pid = INVALID_NUB_PROCESS; 3992 Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); 3993 // Poll until the process is running, as long as we are getting valid 3994 // responses and the timeout hasn't expired 3995 // A return PID of 0 means the process is not running, which may be because 3996 // it hasn't been (asynchronously) started 3997 // yet, or that it died very quickly (if you weren't using waitForDebugger). 3998 while (!pid_found && pid_poll_total < pid_poll_timeout) { 3999 usleep(pid_poll_interval); 4000 pid_poll_total += pid_poll_interval; 4001 DNBLogThreadedIf(LOG_PROCESS, 4002 "%s() polling Springboard for pid for %s...", 4003 __FUNCTION__, bundleID.c_str()); 4004 pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); 4005 } 4006 4007 CFRelease(bundleIDCFStr); 4008 if (pid_found) { 4009 if (process != NULL) { 4010 // Release our primary pty file descriptor so the pty class doesn't 4011 // close it and so we can continue to use it in our STDIO thread 4012 int primary_fd = pty.ReleasePrimaryFD(); 4013 process->SetChildFileDescriptors(primary_fd, primary_fd, primary_fd); 4014 } 4015 DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid); 4016 } else { 4017 DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", 4018 bundleID.c_str()); 4019 } 4020 return pid; 4021 } 4022 4023 DNBLogError("unable to launch the application with CFBundleIdentifier '%s' " 4024 "sbs_error = %u", 4025 bundleID.c_str(), sbs_error); 4026 return INVALID_NUB_PROCESS; 4027} 4028 4029#endif // #ifdef WITH_SPRINGBOARD 4030 4031#if defined(WITH_BKS) || defined(WITH_FBS) 4032pid_t MachProcess::BoardServiceLaunchForDebug( 4033 const char *path, char const *argv[], char const *envp[], bool no_stdio, 4034 bool disable_aslr, const char *event_data, 4035 const RNBContext::IgnoredExceptions &ignored_exceptions, 4036 DNBError &launch_err) { 4037 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); 4038 4039 // Fork a child process for debugging 4040 SetState(eStateLaunching); 4041 m_pid = BoardServiceForkChildForPTraceDebugging( 4042 path, argv, envp, no_stdio, disable_aslr, event_data, launch_err); 4043 if (m_pid != 0) { 4044 m_path = path; 4045 size_t i; 4046 char const *arg; 4047 for (i = 0; (arg = argv[i]) != NULL; i++) 4048 m_args.push_back(arg); 4049 m_task.StartExceptionThread(ignored_exceptions, launch_err); 4050 4051 if (launch_err.Fail()) { 4052 if (launch_err.AsString() == NULL) 4053 launch_err.SetErrorString("unable to start the exception thread"); 4054 DNBLog("[LaunchAttach] END (%d) Could not get inferior's Mach exception " 4055 "port, " 4056 "sending ptrace " 4057 "PT_KILL to pid %i and exiting.", 4058 getpid(), m_pid); 4059 ::ptrace(PT_KILL, m_pid, 0, 0); 4060 m_pid = INVALID_NUB_PROCESS; 4061 return INVALID_NUB_PROCESS; 4062 } 4063 4064 StartSTDIOThread(); 4065 SetState(eStateAttaching); 4066 DNBLog("[LaunchAttach] (%d) About to ptrace(PT_ATTACHEXC, %d)...", getpid(), 4067 m_pid); 4068 int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0); 4069 DNBLog("[LaunchAttach] (%d) Completed ptrace(PT_ATTACHEXC, %d) == %d", 4070 getpid(), m_pid, err); 4071 if (err == 0) { 4072 m_flags |= eMachProcessFlagsAttached; 4073 DNBLog("[LaunchAttach] successfully attached to pid %d", m_pid); 4074 } else { 4075 std::string errmsg = "Failed to attach to pid "; 4076 errmsg += std::to_string(m_pid); 4077 errmsg += ", BoardServiceLaunchForDebug() unable to ptrace(PT_ATTACHEXC)"; 4078 launch_err.SetErrorString(errmsg.c_str()); 4079 SetState(eStateExited); 4080 DNBLog("[LaunchAttach] END (%d) error: failed to attach to pid %d", 4081 getpid(), m_pid); 4082 } 4083 } 4084 return m_pid; 4085} 4086 4087pid_t MachProcess::BoardServiceForkChildForPTraceDebugging( 4088 const char *app_bundle_path, char const *argv[], char const *envp[], 4089 bool no_stdio, bool disable_aslr, const char *event_data, 4090 DNBError &launch_err) { 4091 if (argv[0] == NULL) 4092 return INVALID_NUB_PROCESS; 4093 4094 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, 4095 app_bundle_path, this); 4096 4097 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 4098 4099 size_t argc = 0; 4100 // Count the number of arguments 4101 while (argv[argc] != NULL) 4102 argc++; 4103 4104 // Enumerate the arguments 4105 size_t first_launch_arg_idx = 1; 4106 4107 NSMutableArray *launch_argv = nil; 4108 4109 if (argv[first_launch_arg_idx]) { 4110 size_t launch_argc = argc > 0 ? argc - 1 : 0; 4111 launch_argv = [NSMutableArray arrayWithCapacity:launch_argc]; 4112 size_t i; 4113 char const *arg; 4114 NSString *launch_arg; 4115 for (i = first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); 4116 i++) { 4117 launch_arg = [NSString stringWithUTF8String:arg]; 4118 // FIXME: Should we silently eat an argument that we can't convert into a 4119 // UTF8 string? 4120 if (launch_arg != nil) 4121 [launch_argv addObject:launch_arg]; 4122 else 4123 break; 4124 } 4125 } 4126 4127 NSMutableDictionary *launch_envp = nil; 4128 if (envp[0]) { 4129 launch_envp = [[NSMutableDictionary alloc] init]; 4130 const char *value; 4131 int name_len; 4132 NSString *name_string, *value_string; 4133 4134 for (int i = 0; envp[i] != NULL; i++) { 4135 value = strstr(envp[i], "="); 4136 4137 // If the name field is empty or there's no =, skip it. Somebody's 4138 // messing with us. 4139 if (value == NULL || value == envp[i]) 4140 continue; 4141 4142 name_len = value - envp[i]; 4143 4144 // Now move value over the "=" 4145 value++; 4146 name_string = [[NSString alloc] initWithBytes:envp[i] 4147 length:name_len 4148 encoding:NSUTF8StringEncoding]; 4149 value_string = [NSString stringWithUTF8String:value]; 4150 [launch_envp setObject:value_string forKey:name_string]; 4151 } 4152 } 4153 4154 NSString *stdio_path = nil; 4155 NSFileManager *file_manager = [NSFileManager defaultManager]; 4156 4157 PseudoTerminal pty; 4158 if (!no_stdio) { 4159 PseudoTerminal::Status pty_err = 4160 pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY); 4161 if (pty_err == PseudoTerminal::success) { 4162 const char *secondary_name = pty.SecondaryName(); 4163 DNBLogThreadedIf(LOG_PROCESS, 4164 "%s() successfully opened primary pty, secondary is %s", 4165 __FUNCTION__, secondary_name); 4166 if (secondary_name && secondary_name[0]) { 4167 ::chmod(secondary_name, S_IRWXU | S_IRWXG | S_IRWXO); 4168 stdio_path = [file_manager 4169 stringWithFileSystemRepresentation:secondary_name 4170 length:strlen(secondary_name)]; 4171 } 4172 } 4173 } 4174 4175 if (stdio_path == nil) { 4176 const char *null_path = "/dev/null"; 4177 stdio_path = 4178 [file_manager stringWithFileSystemRepresentation:null_path 4179 length:strlen(null_path)]; 4180 } 4181 4182 CFStringRef bundleIDCFStr = CopyBundleIDForPath(app_bundle_path, launch_err); 4183 if (bundleIDCFStr == NULL) { 4184 [pool drain]; 4185 return INVALID_NUB_PROCESS; 4186 } 4187 4188 // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use 4189 // toll-free bridging here: 4190 NSString *bundleIDNSStr = (NSString *)bundleIDCFStr; 4191 4192 // Okay, now let's assemble all these goodies into the BackBoardServices 4193 // options mega-dictionary: 4194 4195 NSMutableDictionary *options = nullptr; 4196 pid_t return_pid = INVALID_NUB_PROCESS; 4197 bool success = false; 4198 4199#ifdef WITH_BKS 4200 if (ProcessUsingBackBoard()) { 4201 options = 4202 BKSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp, 4203 stdio_path, disable_aslr, event_data); 4204 success = BKSCallOpenApplicationFunction(bundleIDNSStr, options, launch_err, 4205 &return_pid); 4206 } 4207#endif 4208#ifdef WITH_FBS 4209 if (ProcessUsingFrontBoard()) { 4210 options = 4211 FBSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp, 4212 stdio_path, disable_aslr, event_data); 4213 success = FBSCallOpenApplicationFunction(bundleIDNSStr, options, launch_err, 4214 &return_pid); 4215 } 4216#endif 4217 4218 if (success) { 4219 int primary_fd = pty.ReleasePrimaryFD(); 4220 SetChildFileDescriptors(primary_fd, primary_fd, primary_fd); 4221 CFString::UTF8(bundleIDCFStr, m_bundle_id); 4222 } 4223 4224 [pool drain]; 4225 4226 return return_pid; 4227} 4228 4229bool MachProcess::BoardServiceSendEvent(const char *event_data, 4230 DNBError &send_err) { 4231 bool return_value = true; 4232 4233 if (event_data == NULL || *event_data == '\0') { 4234 DNBLogError("SendEvent called with NULL event data."); 4235 send_err.SetErrorString("SendEvent called with empty event data"); 4236 return false; 4237 } 4238 4239 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 4240 4241 if (strcmp(event_data, "BackgroundApplication") == 0) { 4242// This is an event I cooked up. What you actually do is foreground the system 4243// app, so: 4244#ifdef WITH_BKS 4245 if (ProcessUsingBackBoard()) { 4246 return_value = BKSCallOpenApplicationFunction(nil, nil, send_err, NULL); 4247 } 4248#endif 4249#ifdef WITH_FBS 4250 if (ProcessUsingFrontBoard()) { 4251 return_value = FBSCallOpenApplicationFunction(nil, nil, send_err, NULL); 4252 } 4253#endif 4254 if (!return_value) { 4255 DNBLogError("Failed to background application, error: %s.", 4256 send_err.AsString()); 4257 } 4258 } else { 4259 if (m_bundle_id.empty()) { 4260 // See if we can figure out the bundle ID for this PID: 4261 4262 DNBLogError( 4263 "Tried to send event \"%s\" to a process that has no bundle ID.", 4264 event_data); 4265 return false; 4266 } 4267 4268 NSString *bundleIDNSStr = 4269 [NSString stringWithUTF8String:m_bundle_id.c_str()]; 4270 4271 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 4272 4273#ifdef WITH_BKS 4274 if (ProcessUsingBackBoard()) { 4275 if (!BKSAddEventDataToOptions(options, event_data, send_err)) { 4276 [pool drain]; 4277 return false; 4278 } 4279 return_value = BKSCallOpenApplicationFunction(bundleIDNSStr, options, 4280 send_err, NULL); 4281 DNBLogThreadedIf(LOG_PROCESS, 4282 "Called BKSCallOpenApplicationFunction to send event."); 4283 } 4284#endif 4285#ifdef WITH_FBS 4286 if (ProcessUsingFrontBoard()) { 4287 if (!FBSAddEventDataToOptions(options, event_data, send_err)) { 4288 [pool drain]; 4289 return false; 4290 } 4291 return_value = FBSCallOpenApplicationFunction(bundleIDNSStr, options, 4292 send_err, NULL); 4293 DNBLogThreadedIf(LOG_PROCESS, 4294 "Called FBSCallOpenApplicationFunction to send event."); 4295 } 4296#endif 4297 4298 if (!return_value) { 4299 DNBLogError("Failed to send event: %s, error: %s.", event_data, 4300 send_err.AsString()); 4301 } 4302 } 4303 4304 [pool drain]; 4305 return return_value; 4306} 4307#endif // defined(WITH_BKS) || defined (WITH_FBS) 4308 4309#ifdef WITH_BKS 4310void MachProcess::BKSCleanupAfterAttach(const void *attach_token, 4311 DNBError &err_str) { 4312 bool success; 4313 4314 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 4315 4316 // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use 4317 // toll-free bridging here: 4318 NSString *bundleIDNSStr = (NSString *)attach_token; 4319 4320 // Okay, now let's assemble all these goodies into the BackBoardServices 4321 // options mega-dictionary: 4322 4323 // First we have the debug sub-dictionary: 4324 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 4325 [debug_options setObject:[NSNumber numberWithBool:YES] 4326 forKey:BKSDebugOptionKeyCancelDebugOnNextLaunch]; 4327 4328 // That will go in the overall dictionary: 4329 4330 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 4331 [options setObject:debug_options 4332 forKey:BKSOpenApplicationOptionKeyDebuggingOptions]; 4333 4334 success = 4335 BKSCallOpenApplicationFunction(bundleIDNSStr, options, err_str, NULL); 4336 4337 if (!success) { 4338 DNBLogError("error trying to cancel debug on next launch for %s: %s", 4339 [bundleIDNSStr UTF8String], err_str.AsString()); 4340 } 4341 4342 [pool drain]; 4343} 4344#endif // WITH_BKS 4345 4346#ifdef WITH_FBS 4347void MachProcess::FBSCleanupAfterAttach(const void *attach_token, 4348 DNBError &err_str) { 4349 bool success; 4350 4351 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 4352 4353 // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use 4354 // toll-free bridging here: 4355 NSString *bundleIDNSStr = (NSString *)attach_token; 4356 4357 // Okay, now let's assemble all these goodies into the BackBoardServices 4358 // options mega-dictionary: 4359 4360 // First we have the debug sub-dictionary: 4361 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 4362 [debug_options setObject:[NSNumber numberWithBool:YES] 4363 forKey:FBSDebugOptionKeyCancelDebugOnNextLaunch]; 4364 4365 // That will go in the overall dictionary: 4366 4367 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 4368 [options setObject:debug_options 4369 forKey:FBSOpenApplicationOptionKeyDebuggingOptions]; 4370 4371 success = 4372 FBSCallOpenApplicationFunction(bundleIDNSStr, options, err_str, NULL); 4373 4374 if (!success) { 4375 DNBLogError("error trying to cancel debug on next launch for %s: %s", 4376 [bundleIDNSStr UTF8String], err_str.AsString()); 4377 } 4378 4379 [pool drain]; 4380} 4381#endif // WITH_FBS 4382 4383 4384void MachProcess::CalculateBoardStatus() 4385{ 4386 if (m_flags & eMachProcessFlagsBoardCalculated) 4387 return; 4388 if (m_pid == 0) 4389 return; 4390 4391#if defined (WITH_FBS) || defined (WITH_BKS) 4392 bool found_app_flavor = false; 4393#endif 4394 4395#if defined(WITH_FBS) 4396 if (!found_app_flavor && IsFBSProcess(m_pid)) { 4397 found_app_flavor = true; 4398 m_flags |= eMachProcessFlagsUsingFBS; 4399 } 4400#endif 4401#if defined(WITH_BKS) 4402 if (!found_app_flavor && IsBKSProcess(m_pid)) { 4403 found_app_flavor = true; 4404 m_flags |= eMachProcessFlagsUsingBKS; 4405 } 4406#endif 4407 4408 m_flags |= eMachProcessFlagsBoardCalculated; 4409} 4410 4411bool MachProcess::ProcessUsingBackBoard() { 4412 CalculateBoardStatus(); 4413 return (m_flags & eMachProcessFlagsUsingBKS) != 0; 4414} 4415 4416bool MachProcess::ProcessUsingFrontBoard() { 4417 CalculateBoardStatus(); 4418 return (m_flags & eMachProcessFlagsUsingFBS) != 0; 4419} 4420 4421int MachProcess::GetInferiorAddrSize(pid_t pid) { 4422 int pointer_size = 8; 4423 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 4424 struct kinfo_proc processInfo; 4425 size_t bufsize = sizeof(processInfo); 4426 if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo, &bufsize, 4427 NULL, 0) == 0 && 4428 bufsize > 0) { 4429 if ((processInfo.kp_proc.p_flag & P_LP64) == 0) 4430 pointer_size = 4; 4431 } 4432 return pointer_size; 4433} 4434