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