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 <inttypes.h> 16#include <mach/mach.h> 17#include <signal.h> 18#include <spawn.h> 19#include <sys/fcntl.h> 20#include <sys/types.h> 21#include <sys/ptrace.h> 22#include <sys/stat.h> 23#include <sys/sysctl.h> 24#include <unistd.h> 25#include "MacOSX/CFUtils.h" 26#include "SysSignal.h" 27 28#include <algorithm> 29#include <map> 30 31#include "DNBDataRef.h" 32#include "DNBLog.h" 33#include "DNBThreadResumeActions.h" 34#include "DNBTimer.h" 35#include "MachProcess.h" 36#include "PseudoTerminal.h" 37 38#include "CFBundle.h" 39#include "CFData.h" 40#include "CFString.h" 41 42static CFStringRef CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str); 43 44#ifdef WITH_SPRINGBOARD 45 46#include <CoreFoundation/CoreFoundation.h> 47#include <SpringBoardServices/SpringBoardServer.h> 48#include <SpringBoardServices/SBSWatchdogAssertion.h> 49 50static bool 51IsSBProcess (nub_process_t pid) 52{ 53 CFReleaser<CFArrayRef> appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid)); 54 return appIdsForPID.get() != NULL; 55} 56 57#endif // WITH_SPRINGBOARD 58 59#ifdef WITH_BKS 60#import <Foundation/Foundation.h> 61extern "C" 62{ 63#import <BackBoardServices/BackBoardServices.h> 64#import <BackBoardServices/BKSSystemService_LaunchServices.h> 65#import <BackBoardServices/BKSOpenApplicationConstants_Private.h> 66} 67 68static bool 69IsBKSProcess (nub_process_t pid) 70{ 71 BKSApplicationStateMonitor *state_monitor = [[BKSApplicationStateMonitor alloc] init]; 72 BKSApplicationState app_state = [state_monitor mostElevatedApplicationStateForPID: pid]; 73 return app_state != BKSApplicationStateUnknown; 74} 75 76static void 77SetBKSError (BKSOpenApplicationErrorCode error_code, DNBError &error) 78{ 79 error.SetError (error_code, DNBError::BackBoard); 80 NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString(error_code); 81 const char *err_str = NULL; 82 if (err_nsstr == NULL) 83 err_str = "unknown BKS error"; 84 else 85 { 86 err_str = [err_nsstr UTF8String]; 87 if (err_str == NULL) 88 err_str = "unknown BKS error"; 89 } 90 error.SetErrorString(err_str); 91} 92 93static const int BKS_OPEN_APPLICATION_TIMEOUT_ERROR = 111; 94#endif // WITH_BKS 95#if 0 96#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) 97#else 98#define DEBUG_LOG(fmt, ...) 99#endif 100 101#ifndef MACH_PROCESS_USE_POSIX_SPAWN 102#define MACH_PROCESS_USE_POSIX_SPAWN 1 103#endif 104 105#ifndef _POSIX_SPAWN_DISABLE_ASLR 106#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 107#endif 108 109MachProcess::MachProcess() : 110 m_pid (0), 111 m_cpu_type (0), 112 m_child_stdin (-1), 113 m_child_stdout (-1), 114 m_child_stderr (-1), 115 m_path (), 116 m_args (), 117 m_task (this), 118 m_flags (eMachProcessFlagsNone), 119 m_stdio_thread (0), 120 m_stdio_mutex (PTHREAD_MUTEX_RECURSIVE), 121 m_stdout_data (), 122 m_thread_actions (), 123 m_profile_enabled (false), 124 m_profile_interval_usec (0), 125 m_profile_thread (0), 126 m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE), 127 m_profile_data (), 128 m_thread_list (), 129 m_exception_messages (), 130 m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE), 131 m_state (eStateUnloaded), 132 m_state_mutex (PTHREAD_MUTEX_RECURSIVE), 133 m_events (0, kAllEventsMask), 134 m_private_events (0, kAllEventsMask), 135 m_breakpoints (), 136 m_watchpoints (), 137 m_name_to_addr_callback(NULL), 138 m_name_to_addr_baton(NULL), 139 m_image_infos_callback(NULL), 140 m_image_infos_baton(NULL), 141 m_sent_interrupt_signo (0), 142 m_auto_resume_signo (0), 143 m_did_exec (false) 144{ 145 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); 146} 147 148MachProcess::~MachProcess() 149{ 150 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); 151 Clear(); 152} 153 154pid_t 155MachProcess::SetProcessID(pid_t pid) 156{ 157 // Free any previous process specific data or resources 158 Clear(); 159 // Set the current PID appropriately 160 if (pid == 0) 161 m_pid = ::getpid (); 162 else 163 m_pid = pid; 164 return m_pid; // Return actualy PID in case a zero pid was passed in 165} 166 167nub_state_t 168MachProcess::GetState() 169{ 170 // If any other threads access this we will need a mutex for it 171 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); 172 return m_state; 173} 174 175const char * 176MachProcess::ThreadGetName(nub_thread_t tid) 177{ 178 return m_thread_list.GetName(tid); 179} 180 181nub_state_t 182MachProcess::ThreadGetState(nub_thread_t tid) 183{ 184 return m_thread_list.GetState(tid); 185} 186 187 188nub_size_t 189MachProcess::GetNumThreads () const 190{ 191 return m_thread_list.NumThreads(); 192} 193 194nub_thread_t 195MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const 196{ 197 return m_thread_list.ThreadIDAtIndex(thread_idx); 198} 199 200nub_thread_t 201MachProcess::GetThreadIDForMachPortNumber (thread_t mach_port_number) const 202{ 203 return m_thread_list.GetThreadIDByMachPortNumber (mach_port_number); 204} 205 206nub_bool_t 207MachProcess::SyncThreadState (nub_thread_t tid) 208{ 209 MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid)); 210 if (!thread_sp) 211 return false; 212 kern_return_t kret = ::thread_abort_safely(thread_sp->MachPortNumber()); 213 DNBLogThreadedIf (LOG_THREAD, "thread = 0x%8.8" PRIx32 " calling thread_abort_safely (tid) => %u (GetGPRState() for stop_count = %u)", thread_sp->MachPortNumber(), kret, thread_sp->Process()->StopCount()); 214 215 if (kret == KERN_SUCCESS) 216 return true; 217 else 218 return false; 219 220} 221 222nub_thread_t 223MachProcess::GetCurrentThread () 224{ 225 return m_thread_list.CurrentThreadID(); 226} 227 228nub_thread_t 229MachProcess::GetCurrentThreadMachPort () 230{ 231 return m_thread_list.GetMachPortNumberByThreadID(m_thread_list.CurrentThreadID()); 232} 233 234nub_thread_t 235MachProcess::SetCurrentThread(nub_thread_t tid) 236{ 237 return m_thread_list.SetCurrentThread(tid); 238} 239 240bool 241MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) 242{ 243 if (m_thread_list.GetThreadStoppedReason(tid, stop_info)) 244 { 245 if (m_did_exec) 246 stop_info->reason = eStopTypeExec; 247 return true; 248 } 249 return false; 250} 251 252void 253MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const 254{ 255 return m_thread_list.DumpThreadStoppedReason(tid); 256} 257 258const char * 259MachProcess::GetThreadInfo(nub_thread_t tid) const 260{ 261 return m_thread_list.GetThreadInfo(tid); 262} 263 264uint32_t 265MachProcess::GetCPUType () 266{ 267 if (m_cpu_type == 0 && m_pid != 0) 268 m_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid); 269 return m_cpu_type; 270} 271 272const DNBRegisterSetInfo * 273MachProcess::GetRegisterSetInfo (nub_thread_t tid, nub_size_t *num_reg_sets) const 274{ 275 MachThreadSP thread_sp (m_thread_list.GetThreadByID (tid)); 276 if (thread_sp) 277 { 278 DNBArchProtocol *arch = thread_sp->GetArchProtocol(); 279 if (arch) 280 return arch->GetRegisterSetInfo (num_reg_sets); 281 } 282 *num_reg_sets = 0; 283 return NULL; 284} 285 286bool 287MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const 288{ 289 return m_thread_list.GetRegisterValue(tid, set, reg, value); 290} 291 292bool 293MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const 294{ 295 return m_thread_list.SetRegisterValue(tid, set, reg, value); 296} 297 298void 299MachProcess::SetState(nub_state_t new_state) 300{ 301 // If any other threads access this we will need a mutex for it 302 uint32_t event_mask = 0; 303 304 // Scope for mutex locker 305 { 306 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); 307 const nub_state_t old_state = m_state; 308 309 if (old_state == eStateExited) 310 { 311 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring new state since current state is exited", DNBStateAsString(new_state)); 312 } 313 else if (old_state == new_state) 314 { 315 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring redundant state change...", DNBStateAsString(new_state)); 316 } 317 else 318 { 319 if (NUB_STATE_IS_STOPPED(new_state)) 320 event_mask = eEventProcessStoppedStateChanged; 321 else 322 event_mask = eEventProcessRunningStateChanged; 323 324 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) upating state (previous state was %s), event_mask = 0x%8.8x", DNBStateAsString(new_state), DNBStateAsString(old_state), event_mask); 325 326 m_state = new_state; 327 if (new_state == eStateStopped) 328 m_stop_count++; 329 } 330 } 331 332 if (event_mask != 0) 333 { 334 m_events.SetEvents (event_mask); 335 m_private_events.SetEvents (event_mask); 336 if (event_mask == eEventProcessStoppedStateChanged) 337 m_private_events.ResetEvents (eEventProcessRunningStateChanged); 338 else 339 m_private_events.ResetEvents (eEventProcessStoppedStateChanged); 340 341 // Wait for the event bit to reset if a reset ACK is requested 342 m_events.WaitForResetAck(event_mask); 343 } 344 345} 346 347void 348MachProcess::Clear(bool detaching) 349{ 350 // Clear any cached thread list while the pid and task are still valid 351 352 m_task.Clear(); 353 // Now clear out all member variables 354 m_pid = INVALID_NUB_PROCESS; 355 if (!detaching) 356 CloseChildFileDescriptors(); 357 358 m_path.clear(); 359 m_args.clear(); 360 SetState(eStateUnloaded); 361 m_flags = eMachProcessFlagsNone; 362 m_stop_count = 0; 363 m_thread_list.Clear(); 364 { 365 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 366 m_exception_messages.clear(); 367 } 368 if (m_profile_thread) 369 { 370 pthread_join(m_profile_thread, NULL); 371 m_profile_thread = NULL; 372 } 373} 374 375 376bool 377MachProcess::StartSTDIOThread() 378{ 379 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); 380 // Create the thread that watches for the child STDIO 381 return ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this) == 0; 382} 383 384void 385MachProcess::SetEnableAsyncProfiling(bool enable, uint64_t interval_usec, DNBProfileDataScanType scan_type) 386{ 387 m_profile_enabled = enable; 388 m_profile_interval_usec = interval_usec; 389 m_profile_scan_type = scan_type; 390 391 if (m_profile_enabled && (m_profile_thread == NULL)) 392 { 393 StartProfileThread(); 394 } 395 else if (!m_profile_enabled && m_profile_thread) 396 { 397 pthread_join(m_profile_thread, NULL); 398 m_profile_thread = NULL; 399 } 400} 401 402bool 403MachProcess::StartProfileThread() 404{ 405 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); 406 // Create the thread that profiles the inferior and reports back if enabled 407 return ::pthread_create (&m_profile_thread, NULL, MachProcess::ProfileThread, this) == 0; 408} 409 410 411nub_addr_t 412MachProcess::LookupSymbol(const char *name, const char *shlib) 413{ 414 if (m_name_to_addr_callback != NULL && name && name[0]) 415 return m_name_to_addr_callback(ProcessID(), name, shlib, m_name_to_addr_baton); 416 return INVALID_NUB_ADDRESS; 417} 418 419bool 420MachProcess::Resume (const DNBThreadResumeActions& thread_actions) 421{ 422 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()"); 423 nub_state_t state = GetState(); 424 425 if (CanResume(state)) 426 { 427 m_thread_actions = thread_actions; 428 PrivateResume(); 429 return true; 430 } 431 else if (state == eStateRunning) 432 { 433 DNBLog("Resume() - task 0x%x is already running, ignoring...", m_task.TaskPort()); 434 return true; 435 } 436 DNBLog("Resume() - task 0x%x has state %s, can't continue...", m_task.TaskPort(), DNBStateAsString(state)); 437 return false; 438} 439 440bool 441MachProcess::Kill (const struct timespec *timeout_abstime) 442{ 443 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()"); 444 nub_state_t state = DoSIGSTOP(true, false, NULL); 445 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state)); 446 errno = 0; 447 DNBLog ("Sending ptrace PT_KILL to terminate inferior process."); 448 ::ptrace (PT_KILL, m_pid, 0, 0); 449 DNBError err; 450 err.SetErrorToErrno(); 451 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", m_pid, err.Error(), err.AsString()); 452 m_thread_actions = DNBThreadResumeActions (eStateRunning, 0); 453 PrivateResume (); 454 455 // Try and reap the process without touching our m_events since 456 // we want the code above this to still get the eStateExited event 457 const uint32_t reap_timeout_usec = 1000000; // Wait 1 second and try to reap the process 458 const uint32_t reap_interval_usec = 10000; // 459 uint32_t reap_time_elapsed; 460 for (reap_time_elapsed = 0; 461 reap_time_elapsed < reap_timeout_usec; 462 reap_time_elapsed += reap_interval_usec) 463 { 464 if (GetState() == eStateExited) 465 break; 466 usleep(reap_interval_usec); 467 } 468 DNBLog ("Waited %u ms for process to be reaped (state = %s)", reap_time_elapsed/1000, DNBStateAsString(GetState())); 469 return true; 470} 471 472bool 473MachProcess::Interrupt() 474{ 475 nub_state_t state = GetState(); 476 if (IsRunning(state)) 477 { 478 if (m_sent_interrupt_signo == 0) 479 { 480 m_sent_interrupt_signo = SIGSTOP; 481 if (Signal (m_sent_interrupt_signo)) 482 { 483 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - sent %i signal to interrupt process", m_sent_interrupt_signo); 484 } 485 else 486 { 487 m_sent_interrupt_signo = 0; 488 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - failed to send %i signal to interrupt process", m_sent_interrupt_signo); 489 } 490 } 491 else 492 { 493 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - previously sent an interrupt signal %i that hasn't been received yet, interrupt aborted", m_sent_interrupt_signo); 494 } 495 } 496 else 497 { 498 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - process already stopped, no interrupt sent"); 499 } 500 return false; 501} 502 503bool 504MachProcess::Signal (int signal, const struct timespec *timeout_abstime) 505{ 506 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p)", signal, timeout_abstime); 507 nub_state_t state = GetState(); 508 if (::kill (ProcessID(), signal) == 0) 509 { 510 // If we were running and we have a timeout, wait for the signal to stop 511 if (IsRunning(state) && timeout_abstime) 512 { 513 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime); 514 m_private_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime); 515 state = GetState(); 516 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state)); 517 return !IsRunning (state); 518 } 519 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", signal, timeout_abstime); 520 return true; 521 } 522 DNBError err(errno, DNBError::POSIX); 523 err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal); 524 return false; 525 526} 527 528bool 529MachProcess::SendEvent (const char *event, DNBError &send_err) 530{ 531 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SendEvent (event = %s) to pid: %d", event, m_pid); 532 if (m_pid == INVALID_NUB_PROCESS) 533 return false; 534#if WITH_BKS 535 return BKSSendEvent (event, send_err); 536#endif 537 return true; 538} 539 540nub_state_t 541MachProcess::DoSIGSTOP (bool clear_bps_and_wps, bool allow_running, uint32_t *thread_idx_ptr) 542{ 543 nub_state_t state = GetState(); 544 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", DNBStateAsString (state)); 545 546 if (!IsRunning(state)) 547 { 548 if (clear_bps_and_wps) 549 { 550 DisableAllBreakpoints (true); 551 DisableAllWatchpoints (true); 552 clear_bps_and_wps = false; 553 } 554 555 // If we already have a thread stopped due to a SIGSTOP, we don't have 556 // to do anything... 557 uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP); 558 if (thread_idx_ptr) 559 *thread_idx_ptr = thread_idx; 560 if (thread_idx != UINT32_MAX) 561 return GetState(); 562 563 // No threads were stopped with a SIGSTOP, we need to run and halt the 564 // process with a signal 565 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state)); 566 if (allow_running) 567 m_thread_actions = DNBThreadResumeActions (eStateRunning, 0); 568 else 569 m_thread_actions = DNBThreadResumeActions (eStateSuspended, 0); 570 571 PrivateResume (); 572 573 // Reset the event that says we were indeed running 574 m_events.ResetEvents(eEventProcessRunningStateChanged); 575 state = GetState(); 576 } 577 578 // We need to be stopped in order to be able to detach, so we need 579 // to send ourselves a SIGSTOP 580 581 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", DNBStateAsString (state)); 582 struct timespec sigstop_timeout; 583 DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0); 584 Signal (SIGSTOP, &sigstop_timeout); 585 if (clear_bps_and_wps) 586 { 587 DisableAllBreakpoints (true); 588 DisableAllWatchpoints (true); 589 //clear_bps_and_wps = false; 590 } 591 uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP); 592 if (thread_idx_ptr) 593 *thread_idx_ptr = thread_idx; 594 return GetState(); 595} 596 597bool 598MachProcess::Detach() 599{ 600 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()"); 601 602 uint32_t thread_idx = UINT32_MAX; 603 nub_state_t state = DoSIGSTOP(true, true, &thread_idx); 604 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state)); 605 606 { 607 m_thread_actions.Clear(); 608 DNBThreadResumeAction thread_action; 609 thread_action.tid = m_thread_list.ThreadIDAtIndex (thread_idx); 610 thread_action.state = eStateRunning; 611 thread_action.signal = -1; 612 thread_action.addr = INVALID_NUB_ADDRESS; 613 614 m_thread_actions.Append (thread_action); 615 m_thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0); 616 617 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 618 619 ReplyToAllExceptions (); 620 621 } 622 623 m_task.ShutDownExcecptionThread(); 624 625 // Detach from our process 626 errno = 0; 627 nub_process_t pid = m_pid; 628 int ret = ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); 629 DNBError err(errno, DNBError::POSIX); 630 if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0)) 631 err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); 632 633 // Resume our task 634 m_task.Resume(); 635 636 // NULL our task out as we have already retored all exception ports 637 m_task.Clear(); 638 639 // Clear out any notion of the process we once were 640 const bool detaching = true; 641 Clear(detaching); 642 643 SetState(eStateDetached); 644 645 return true; 646} 647 648//---------------------------------------------------------------------- 649// ReadMemory from the MachProcess level will always remove any software 650// breakpoints from the memory buffer before returning. If you wish to 651// read memory and see those traps, read from the MachTask 652// (m_task.ReadMemory()) as that version will give you what is actually 653// in inferior memory. 654//---------------------------------------------------------------------- 655nub_size_t 656MachProcess::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf) 657{ 658 // We need to remove any current software traps (enabled software 659 // breakpoints) that we may have placed in our tasks memory. 660 661 // First just read the memory as is 662 nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf); 663 664 // Then place any opcodes that fall into this range back into the buffer 665 // before we return this to callers. 666 if (bytes_read > 0) 667 m_breakpoints.RemoveTrapsFromBuffer (addr, bytes_read, buf); 668 return bytes_read; 669} 670 671//---------------------------------------------------------------------- 672// WriteMemory from the MachProcess level will always write memory around 673// any software breakpoints. Any software breakpoints will have their 674// opcodes modified if they are enabled. Any memory that doesn't overlap 675// with software breakpoints will be written to. If you wish to write to 676// inferior memory without this interference, then write to the MachTask 677// (m_task.WriteMemory()) as that version will always modify inferior 678// memory. 679//---------------------------------------------------------------------- 680nub_size_t 681MachProcess::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf) 682{ 683 // We need to write any data that would go where any current software traps 684 // (enabled software breakpoints) any software traps (breakpoints) that we 685 // may have placed in our tasks memory. 686 687 std::vector<DNBBreakpoint *> bps; 688 689 const size_t num_bps = m_breakpoints.FindBreakpointsThatOverlapRange(addr, size, bps); 690 if (num_bps == 0) 691 return m_task.WriteMemory(addr, size, buf); 692 693 nub_size_t bytes_written = 0; 694 nub_addr_t intersect_addr; 695 nub_size_t intersect_size; 696 nub_size_t opcode_offset; 697 const uint8_t *ubuf = (const uint8_t *)buf; 698 699 for (size_t i=0; i<num_bps; ++i) 700 { 701 DNBBreakpoint *bp = bps[i]; 702 703 const bool intersects = bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset); 704 assert(intersects); 705 assert(addr <= intersect_addr && intersect_addr < addr + size); 706 assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); 707 assert(opcode_offset + intersect_size <= bp->ByteSize()); 708 709 // Check for bytes before this breakpoint 710 const nub_addr_t curr_addr = addr + bytes_written; 711 if (intersect_addr > curr_addr) 712 { 713 // There are some bytes before this breakpoint that we need to 714 // just write to memory 715 nub_size_t curr_size = intersect_addr - curr_addr; 716 nub_size_t curr_bytes_written = m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written); 717 bytes_written += curr_bytes_written; 718 if (curr_bytes_written != curr_size) 719 { 720 // We weren't able to write all of the requested bytes, we 721 // are done looping and will return the number of bytes that 722 // we have written so far. 723 break; 724 } 725 } 726 727 // Now write any bytes that would cover up any software breakpoints 728 // directly into the breakpoint opcode buffer 729 ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size); 730 bytes_written += intersect_size; 731 } 732 733 // Write any remaining bytes after the last breakpoint if we have any left 734 if (bytes_written < size) 735 bytes_written += m_task.WriteMemory(addr + bytes_written, size - bytes_written, ubuf + bytes_written); 736 737 return bytes_written; 738} 739 740void 741MachProcess::ReplyToAllExceptions () 742{ 743 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 744 if (m_exception_messages.empty() == false) 745 { 746 MachException::Message::iterator pos; 747 MachException::Message::iterator begin = m_exception_messages.begin(); 748 MachException::Message::iterator end = m_exception_messages.end(); 749 for (pos = begin; pos != end; ++pos) 750 { 751 DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %u...", (uint32_t)std::distance(begin, pos)); 752 int thread_reply_signal = 0; 753 754 nub_thread_t tid = m_thread_list.GetThreadIDByMachPortNumber (pos->state.thread_port); 755 const DNBThreadResumeAction *action = NULL; 756 if (tid != INVALID_NUB_THREAD) 757 { 758 action = m_thread_actions.GetActionForThread (tid, false); 759 } 760 761 if (action) 762 { 763 thread_reply_signal = action->signal; 764 if (thread_reply_signal) 765 m_thread_actions.SetSignalHandledForThread (tid); 766 } 767 768 DNBError err (pos->Reply(this, thread_reply_signal)); 769 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 770 err.LogThreadedIfError("Error replying to exception"); 771 } 772 773 // Erase all exception message as we should have used and replied 774 // to them all already. 775 m_exception_messages.clear(); 776 } 777} 778void 779MachProcess::PrivateResume () 780{ 781 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 782 783 m_auto_resume_signo = m_sent_interrupt_signo; 784 if (m_auto_resume_signo) 785 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x resuming (with unhandled interrupt signal %i)...", m_task.TaskPort(), m_auto_resume_signo); 786 else 787 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x resuming...", m_task.TaskPort()); 788 789 ReplyToAllExceptions (); 790// bool stepOverBreakInstruction = step; 791 792 // Let the thread prepare to resume and see if any threads want us to 793 // step over a breakpoint instruction (ProcessWillResume will modify 794 // the value of stepOverBreakInstruction). 795 m_thread_list.ProcessWillResume (this, m_thread_actions); 796 797 // Set our state accordingly 798 if (m_thread_actions.NumActionsWithState(eStateStepping)) 799 SetState (eStateStepping); 800 else 801 SetState (eStateRunning); 802 803 // Now resume our task. 804 m_task.Resume(); 805} 806 807DNBBreakpoint * 808MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware) 809{ 810 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %llu, hardware = %i)", (uint64_t)addr, (uint64_t)length, hardware); 811 812 DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); 813 if (bp) 814 bp->Retain(); 815 else 816 bp = m_breakpoints.Add(addr, length, hardware); 817 818 if (EnableBreakpoint(addr)) 819 { 820 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %llu) => %p", (uint64_t)addr, (uint64_t)length, bp); 821 return bp; 822 } 823 else if (bp->Release() == 0) 824 { 825 m_breakpoints.Remove(addr); 826 } 827 // We failed to enable the breakpoint 828 return NULL; 829} 830 831DNBBreakpoint * 832MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware) 833{ 834 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu, flags = 0x%8.8x, hardware = %i)", (uint64_t)addr, (uint64_t)length, watch_flags, hardware); 835 836 DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); 837 // since the Z packets only send an address, we can only have one watchpoint at 838 // an address. If there is already one, we must refuse to create another watchpoint 839 if (wp) 840 return NULL; 841 842 wp = m_watchpoints.Add(addr, length, hardware); 843 wp->SetIsWatchpoint(watch_flags); 844 845 if (EnableWatchpoint(addr)) 846 { 847 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu) => %p", (uint64_t)addr, (uint64_t)length, wp); 848 return wp; 849 } 850 else 851 { 852 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu) => FAILED", (uint64_t)addr, (uint64_t)length); 853 m_watchpoints.Remove(addr); 854 } 855 // We failed to enable the watchpoint 856 return NULL; 857} 858 859void 860MachProcess::DisableAllBreakpoints (bool remove) 861{ 862 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); 863 864 m_breakpoints.DisableAllBreakpoints (this); 865 866 if (remove) 867 m_breakpoints.RemoveDisabled(); 868} 869 870void 871MachProcess::DisableAllWatchpoints(bool remove) 872{ 873 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); 874 875 m_watchpoints.DisableAllWatchpoints(this); 876 877 if (remove) 878 m_watchpoints.RemoveDisabled(); 879} 880 881bool 882MachProcess::DisableBreakpoint(nub_addr_t addr, bool remove) 883{ 884 DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); 885 if (bp) 886 { 887 // After "exec" we might end up with a bunch of breakpoints that were disabled 888 // manually, just ignore them 889 if (!bp->IsEnabled()) 890 { 891 // Breakpoint might have been disabled by an exec 892 if (remove && bp->Release() == 0) 893 { 894 m_thread_list.NotifyBreakpointChanged(bp); 895 m_breakpoints.Remove(addr); 896 } 897 return true; 898 } 899 900 // We have multiple references to this breakpoint, decrement the ref count 901 // and if it isn't zero, then return true; 902 if (remove && bp->Release() > 0) 903 return true; 904 905 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d )", (uint64_t)addr, remove); 906 907 if (bp->IsHardware()) 908 { 909 bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint (bp); 910 911 if (hw_disable_result == true) 912 { 913 bp->SetEnabled(false); 914 // Let the thread list know that a breakpoint has been modified 915 if (remove) 916 { 917 m_thread_list.NotifyBreakpointChanged(bp); 918 m_breakpoints.Remove(addr); 919 } 920 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) (hardware) => success", (uint64_t)addr, remove); 921 return true; 922 } 923 924 return false; 925 } 926 927 const nub_size_t break_op_size = bp->ByteSize(); 928 assert (break_op_size > 0); 929 const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (bp->ByteSize()); 930 if (break_op_size > 0) 931 { 932 // Clear a software breakoint instruction 933 uint8_t curr_break_op[break_op_size]; 934 bool break_op_found = false; 935 936 // Read the breakpoint opcode 937 if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == break_op_size) 938 { 939 bool verify = false; 940 if (bp->IsEnabled()) 941 { 942 // Make sure we have the a breakpoint opcode exists at this address 943 if (memcmp(curr_break_op, break_op, break_op_size) == 0) 944 { 945 break_op_found = true; 946 // We found a valid breakpoint opcode at this address, now restore 947 // the saved opcode. 948 if (m_task.WriteMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size) 949 { 950 verify = true; 951 } 952 else 953 { 954 DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) memory write failed when restoring original opcode", (uint64_t)addr, remove); 955 } 956 } 957 else 958 { 959 DNBLogWarning("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) expected a breakpoint opcode but didn't find one.", (uint64_t)addr, remove); 960 // Set verify to true and so we can check if the original opcode has already been restored 961 verify = true; 962 } 963 } 964 else 965 { 966 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) is not enabled", (uint64_t)addr, remove); 967 // Set verify to true and so we can check if the original opcode is there 968 verify = true; 969 } 970 971 if (verify) 972 { 973 uint8_t verify_opcode[break_op_size]; 974 // Verify that our original opcode made it back to the inferior 975 if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == break_op_size) 976 { 977 // compare the memory we just read with the original opcode 978 if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 0) 979 { 980 // SUCCESS 981 bp->SetEnabled(false); 982 // Let the thread list know that a breakpoint has been modified 983 if (remove && bp->Release() == 0) 984 { 985 m_thread_list.NotifyBreakpointChanged(bp); 986 m_breakpoints.Remove(addr); 987 } 988 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) => success", (uint64_t)addr, remove); 989 return true; 990 } 991 else 992 { 993 if (break_op_found) 994 DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) : failed to restore original opcode", (uint64_t)addr, remove); 995 else 996 DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) : opcode changed", (uint64_t)addr, remove); 997 } 998 } 999 else 1000 { 1001 DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable breakpoint 0x%8.8llx", (uint64_t)addr); 1002 } 1003 } 1004 } 1005 else 1006 { 1007 DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory at 0x%8.8llx", (uint64_t)addr); 1008 } 1009 } 1010 } 1011 else 1012 { 1013 DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) invalid breakpoint address", (uint64_t)addr, remove); 1014 } 1015 return false; 1016} 1017 1018bool 1019MachProcess::DisableWatchpoint(nub_addr_t addr, bool remove) 1020{ 1021 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(addr = 0x%8.8llx, remove = %d)", __FUNCTION__, (uint64_t)addr, remove); 1022 DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); 1023 if (wp) 1024 { 1025 // If we have multiple references to a watchpoint, removing the watchpoint shouldn't clear it 1026 if (remove && wp->Release() > 0) 1027 return true; 1028 1029 nub_addr_t addr = wp->Address(); 1030 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d )", (uint64_t)addr, remove); 1031 1032 if (wp->IsHardware()) 1033 { 1034 bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint (wp); 1035 1036 if (hw_disable_result == true) 1037 { 1038 wp->SetEnabled(false); 1039 if (remove) 1040 m_watchpoints.Remove(addr); 1041 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( addr = 0x%8.8llx, remove = %d ) (hardware) => success", (uint64_t)addr, remove); 1042 return true; 1043 } 1044 } 1045 1046 // TODO: clear software watchpoints if we implement them 1047 } 1048 else 1049 { 1050 DNBLogError("MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d ) invalid watchpoint ID", (uint64_t)addr, remove); 1051 } 1052 return false; 1053} 1054 1055 1056uint32_t 1057MachProcess::GetNumSupportedHardwareWatchpoints () const 1058{ 1059 return m_thread_list.NumSupportedHardwareWatchpoints(); 1060} 1061 1062bool 1063MachProcess::EnableBreakpoint(nub_addr_t addr) 1064{ 1065 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx )", (uint64_t)addr); 1066 DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); 1067 if (bp) 1068 { 1069 if (bp->IsEnabled()) 1070 { 1071 DNBLogWarning("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): breakpoint already enabled.", (uint64_t)addr); 1072 return true; 1073 } 1074 else 1075 { 1076 if (bp->HardwarePreferred()) 1077 { 1078 bp->SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp)); 1079 if (bp->IsHardware()) 1080 { 1081 bp->SetEnabled(true); 1082 return true; 1083 } 1084 } 1085 1086 const nub_size_t break_op_size = bp->ByteSize(); 1087 assert (break_op_size != 0); 1088 const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (break_op_size); 1089 if (break_op_size > 0) 1090 { 1091 // Save the original opcode by reading it 1092 if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size) 1093 { 1094 // Write a software breakpoint in place of the original opcode 1095 if (m_task.WriteMemory(addr, break_op_size, break_op) == break_op_size) 1096 { 1097 uint8_t verify_break_op[4]; 1098 if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == break_op_size) 1099 { 1100 if (memcmp(break_op, verify_break_op, break_op_size) == 0) 1101 { 1102 bp->SetEnabled(true); 1103 // Let the thread list know that a breakpoint has been modified 1104 m_thread_list.NotifyBreakpointChanged(bp); 1105 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) : SUCCESS.", (uint64_t)addr); 1106 return true; 1107 } 1108 else 1109 { 1110 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): breakpoint opcode verification failed.", (uint64_t)addr); 1111 } 1112 } 1113 else 1114 { 1115 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to read memory to verify breakpoint opcode.", (uint64_t)addr); 1116 } 1117 } 1118 else 1119 { 1120 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to write breakpoint opcode to memory.", (uint64_t)addr); 1121 } 1122 } 1123 else 1124 { 1125 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to read memory at breakpoint address.", (uint64_t)addr); 1126 } 1127 } 1128 else 1129 { 1130 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) no software breakpoint opcode for current architecture.", (uint64_t)addr); 1131 } 1132 } 1133 } 1134 return false; 1135} 1136 1137bool 1138MachProcess::EnableWatchpoint(nub_addr_t addr) 1139{ 1140 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(addr = 0x%8.8llx)", (uint64_t)addr); 1141 DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); 1142 if (wp) 1143 { 1144 nub_addr_t addr = wp->Address(); 1145 if (wp->IsEnabled()) 1146 { 1147 DNBLogWarning("MachProcess::EnableWatchpoint(addr = 0x%8.8llx): watchpoint already enabled.", (uint64_t)addr); 1148 return true; 1149 } 1150 else 1151 { 1152 // Currently only try and set hardware watchpoints. 1153 wp->SetHardwareIndex(m_thread_list.EnableHardwareWatchpoint(wp)); 1154 if (wp->IsHardware()) 1155 { 1156 wp->SetEnabled(true); 1157 return true; 1158 } 1159 // TODO: Add software watchpoints by doing page protection tricks. 1160 } 1161 } 1162 return false; 1163} 1164 1165// Called by the exception thread when an exception has been received from 1166// our process. The exception message is completely filled and the exception 1167// data has already been copied. 1168void 1169MachProcess::ExceptionMessageReceived (const MachException::Message& exceptionMessage) 1170{ 1171 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 1172 1173 if (m_exception_messages.empty()) 1174 m_task.Suspend(); 1175 1176 DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )"); 1177 1178 // Use a locker to automatically unlock our mutex in case of exceptions 1179 // Add the exception to our internal exception stack 1180 m_exception_messages.push_back(exceptionMessage); 1181} 1182 1183task_t 1184MachProcess::ExceptionMessageBundleComplete() 1185{ 1186 // We have a complete bundle of exceptions for our child process. 1187 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 1188 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %llu exception messages.", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size()); 1189 bool auto_resume = false; 1190 if (!m_exception_messages.empty()) 1191 { 1192 m_did_exec = false; 1193 // First check for any SIGTRAP and make sure we didn't exec 1194 const task_t task = m_task.TaskPort(); 1195 size_t i; 1196 if (m_pid != 0) 1197 { 1198 bool received_interrupt = false; 1199 uint32_t num_task_exceptions = 0; 1200 for (i=0; i<m_exception_messages.size(); ++i) 1201 { 1202 if (m_exception_messages[i].state.task_port == task) 1203 { 1204 ++num_task_exceptions; 1205 const int signo = m_exception_messages[i].state.SoftSignal(); 1206 if (signo == SIGTRAP) 1207 { 1208 // SIGTRAP could mean that we exec'ed. We need to check the 1209 // dyld all_image_infos.infoArray to see if it is NULL and if 1210 // so, say that we exec'ed. 1211 const nub_addr_t aii_addr = GetDYLDAllImageInfosAddress(); 1212 if (aii_addr != INVALID_NUB_ADDRESS) 1213 { 1214 const nub_addr_t info_array_count_addr = aii_addr + 4; 1215 uint32_t info_array_count = 0; 1216 if (m_task.ReadMemory(info_array_count_addr, 4, &info_array_count) == 4) 1217 { 1218 if (info_array_count == 0) 1219 { 1220 m_did_exec = true; 1221 // Force the task port to update itself in case the task port changed after exec 1222 DNBError err; 1223 const task_t old_task = m_task.TaskPort(); 1224 const task_t new_task = m_task.TaskPortForProcessID (err, true); 1225 if (old_task != new_task) 1226 DNBLogThreadedIf(LOG_PROCESS, "exec: task changed from 0x%4.4x to 0x%4.4x", old_task, new_task); 1227 } 1228 } 1229 else 1230 { 1231 DNBLog ("error: failed to read all_image_infos.infoArrayCount from 0x%8.8llx", (uint64_t)info_array_count_addr); 1232 } 1233 } 1234 break; 1235 } 1236 else if (m_sent_interrupt_signo != 0 && signo == m_sent_interrupt_signo) 1237 { 1238 received_interrupt = true; 1239 } 1240 } 1241 } 1242 1243 if (m_did_exec) 1244 { 1245 cpu_type_t process_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid); 1246 if (m_cpu_type != process_cpu_type) 1247 { 1248 DNBLog ("arch changed from 0x%8.8x to 0x%8.8x", m_cpu_type, process_cpu_type); 1249 m_cpu_type = process_cpu_type; 1250 DNBArchProtocol::SetArchitecture (process_cpu_type); 1251 } 1252 m_thread_list.Clear(); 1253 m_breakpoints.DisableAll(); 1254 } 1255 1256 if (m_sent_interrupt_signo != 0) 1257 { 1258 if (received_interrupt) 1259 { 1260 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): process successfully interrupted with signal %i", m_sent_interrupt_signo); 1261 1262 // Mark that we received the interrupt signal 1263 m_sent_interrupt_signo = 0; 1264 // Not check if we had a case where: 1265 // 1 - We called MachProcess::Interrupt() but we stopped for another reason 1266 // 2 - We called MachProcess::Resume() (but still haven't gotten the interrupt signal) 1267 // 3 - We are now incorrectly stopped because we are handling the interrupt signal we missed 1268 // 4 - We might need to resume if we stopped only with the interrupt signal that we never handled 1269 if (m_auto_resume_signo != 0) 1270 { 1271 // Only auto_resume if we stopped with _only_ the interrupt signal 1272 if (num_task_exceptions == 1) 1273 { 1274 auto_resume = true; 1275 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): auto resuming due to unhandled interrupt signal %i", m_auto_resume_signo); 1276 } 1277 m_auto_resume_signo = 0; 1278 } 1279 } 1280 else 1281 { 1282 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): didn't get signal %i after MachProcess::Interrupt()", 1283 m_sent_interrupt_signo); 1284 } 1285 } 1286 } 1287 1288 // Let all threads recover from stopping and do any clean up based 1289 // on the previous thread state (if any). 1290 m_thread_list.ProcessDidStop(this); 1291 1292 // Let each thread know of any exceptions 1293 for (i=0; i<m_exception_messages.size(); ++i) 1294 { 1295 // Let the thread list figure use the MachProcess to forward all exceptions 1296 // on down to each thread. 1297 if (m_exception_messages[i].state.task_port == task) 1298 m_thread_list.NotifyException(m_exception_messages[i].state); 1299 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 1300 m_exception_messages[i].Dump(); 1301 } 1302 1303 if (DNBLogCheckLogBit(LOG_THREAD)) 1304 m_thread_list.Dump(); 1305 1306 bool step_more = false; 1307 if (m_thread_list.ShouldStop(step_more) && auto_resume == false) 1308 { 1309 // Wait for the eEventProcessRunningStateChanged event to be reset 1310 // before changing state to stopped to avoid race condition with 1311 // very fast start/stops 1312 struct timespec timeout; 1313 //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000); // Wait for 250 ms 1314 DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms 1315 m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout); 1316 SetState(eStateStopped); 1317 } 1318 else 1319 { 1320 // Resume without checking our current state. 1321 PrivateResume (); 1322 } 1323 } 1324 else 1325 { 1326 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s empty exception messages bundle (%llu exceptions).", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size()); 1327 } 1328 return m_task.TaskPort(); 1329} 1330 1331nub_size_t 1332MachProcess::CopyImageInfos ( struct DNBExecutableImageInfo **image_infos, bool only_changed) 1333{ 1334 if (m_image_infos_callback != NULL) 1335 return m_image_infos_callback(ProcessID(), image_infos, only_changed, m_image_infos_baton); 1336 return 0; 1337} 1338 1339void 1340MachProcess::SharedLibrariesUpdated ( ) 1341{ 1342 uint32_t event_bits = eEventSharedLibsStateChange; 1343 // Set the shared library event bit to let clients know of shared library 1344 // changes 1345 m_events.SetEvents(event_bits); 1346 // Wait for the event bit to reset if a reset ACK is requested 1347 m_events.WaitForResetAck(event_bits); 1348} 1349 1350void 1351MachProcess::SetExitInfo (const char *info) 1352{ 1353 if (info && info[0]) 1354 { 1355 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(\"%s\")", __FUNCTION__, info); 1356 m_exit_info.assign(info); 1357 } 1358 else 1359 { 1360 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(NULL)", __FUNCTION__); 1361 m_exit_info.clear(); 1362 } 1363} 1364 1365void 1366MachProcess::AppendSTDOUT (char* s, size_t len) 1367{ 1368 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%llu> %s) ...", __FUNCTION__, (uint64_t)len, s); 1369 PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex); 1370 m_stdout_data.append(s, len); 1371 m_events.SetEvents(eEventStdioAvailable); 1372 1373 // Wait for the event bit to reset if a reset ACK is requested 1374 m_events.WaitForResetAck(eEventStdioAvailable); 1375} 1376 1377size_t 1378MachProcess::GetAvailableSTDOUT (char *buf, size_t buf_size) 1379{ 1380 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, buf, (uint64_t)buf_size); 1381 PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex); 1382 size_t bytes_available = m_stdout_data.size(); 1383 if (bytes_available > 0) 1384 { 1385 if (bytes_available > buf_size) 1386 { 1387 memcpy(buf, m_stdout_data.data(), buf_size); 1388 m_stdout_data.erase(0, buf_size); 1389 bytes_available = buf_size; 1390 } 1391 else 1392 { 1393 memcpy(buf, m_stdout_data.data(), bytes_available); 1394 m_stdout_data.clear(); 1395 } 1396 } 1397 return bytes_available; 1398} 1399 1400nub_addr_t 1401MachProcess::GetDYLDAllImageInfosAddress () 1402{ 1403 DNBError err; 1404 return m_task.GetDYLDAllImageInfosAddress(err); 1405} 1406 1407size_t 1408MachProcess::GetAvailableSTDERR (char *buf, size_t buf_size) 1409{ 1410 return 0; 1411} 1412 1413void * 1414MachProcess::STDIOThread(void *arg) 1415{ 1416 MachProcess *proc = (MachProcess*) arg; 1417 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg); 1418 1419 // We start use a base and more options so we can control if we 1420 // are currently using a timeout on the mach_msg. We do this to get a 1421 // bunch of related exceptions on our exception port so we can process 1422 // then together. When we have multiple threads, we can get an exception 1423 // per thread and they will come in consecutively. The main thread loop 1424 // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT 1425 // flag set in the options, so we will wait forever for an exception on 1426 // our exception port. After we get one exception, we then will use the 1427 // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current 1428 // exceptions for our process. After we have received the last pending 1429 // exception, we will get a timeout which enables us to then notify 1430 // our main thread that we have an exception bundle avaiable. We then wait 1431 // for the main thread to tell this exception thread to start trying to get 1432 // exceptions messages again and we start again with a mach_msg read with 1433 // infinite timeout. 1434 DNBError err; 1435 int stdout_fd = proc->GetStdoutFileDescriptor(); 1436 int stderr_fd = proc->GetStderrFileDescriptor(); 1437 if (stdout_fd == stderr_fd) 1438 stderr_fd = -1; 1439 1440 while (stdout_fd >= 0 || stderr_fd >= 0) 1441 { 1442 ::pthread_testcancel (); 1443 1444 fd_set read_fds; 1445 FD_ZERO (&read_fds); 1446 if (stdout_fd >= 0) 1447 FD_SET (stdout_fd, &read_fds); 1448 if (stderr_fd >= 0) 1449 FD_SET (stderr_fd, &read_fds); 1450 int nfds = std::max<int>(stdout_fd, stderr_fd) + 1; 1451 1452 int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL); 1453 DNBLogThreadedIf(LOG_PROCESS, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); 1454 1455 if (num_set_fds < 0) 1456 { 1457 int select_errno = errno; 1458 if (DNBLogCheckLogBit(LOG_PROCESS)) 1459 { 1460 err.SetError (select_errno, DNBError::POSIX); 1461 err.LogThreadedIfError("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); 1462 } 1463 1464 switch (select_errno) 1465 { 1466 case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO 1467 break; 1468 case EBADF: // One of the descriptor sets specified an invalid descriptor. 1469 return NULL; 1470 break; 1471 case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred. 1472 case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. 1473 default: // Other unknown error 1474 break; 1475 } 1476 } 1477 else if (num_set_fds == 0) 1478 { 1479 } 1480 else 1481 { 1482 char s[1024]; 1483 s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination 1484 int bytes_read = 0; 1485 if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds)) 1486 { 1487 do 1488 { 1489 bytes_read = ::read (stdout_fd, s, sizeof(s)-1); 1490 if (bytes_read < 0) 1491 { 1492 int read_errno = errno; 1493 DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); 1494 } 1495 else if (bytes_read == 0) 1496 { 1497 // EOF... 1498 DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read); 1499 stdout_fd = -1; 1500 } 1501 else if (bytes_read > 0) 1502 { 1503 proc->AppendSTDOUT(s, bytes_read); 1504 } 1505 1506 } while (bytes_read > 0); 1507 } 1508 1509 if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds)) 1510 { 1511 do 1512 { 1513 bytes_read = ::read (stderr_fd, s, sizeof(s)-1); 1514 if (bytes_read < 0) 1515 { 1516 int read_errno = errno; 1517 DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); 1518 } 1519 else if (bytes_read == 0) 1520 { 1521 // EOF... 1522 DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read); 1523 stderr_fd = -1; 1524 } 1525 else if (bytes_read > 0) 1526 { 1527 proc->AppendSTDOUT(s, bytes_read); 1528 } 1529 1530 } while (bytes_read > 0); 1531 } 1532 } 1533 } 1534 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", __FUNCTION__, arg); 1535 return NULL; 1536} 1537 1538 1539void 1540MachProcess::SignalAsyncProfileData (const char *info) 1541{ 1542 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%s) ...", __FUNCTION__, info); 1543 PTHREAD_MUTEX_LOCKER (locker, m_profile_data_mutex); 1544 m_profile_data.push_back(info); 1545 m_events.SetEvents(eEventProfileDataAvailable); 1546 1547 // Wait for the event bit to reset if a reset ACK is requested 1548 m_events.WaitForResetAck(eEventProfileDataAvailable); 1549} 1550 1551 1552size_t 1553MachProcess::GetAsyncProfileData (char *buf, size_t buf_size) 1554{ 1555 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, buf, (uint64_t)buf_size); 1556 PTHREAD_MUTEX_LOCKER (locker, m_profile_data_mutex); 1557 if (m_profile_data.empty()) 1558 return 0; 1559 1560 size_t bytes_available = m_profile_data.front().size(); 1561 if (bytes_available > 0) 1562 { 1563 if (bytes_available > buf_size) 1564 { 1565 memcpy(buf, m_profile_data.front().data(), buf_size); 1566 m_profile_data.front().erase(0, buf_size); 1567 bytes_available = buf_size; 1568 } 1569 else 1570 { 1571 memcpy(buf, m_profile_data.front().data(), bytes_available); 1572 m_profile_data.erase(m_profile_data.begin()); 1573 } 1574 } 1575 return bytes_available; 1576} 1577 1578 1579void * 1580MachProcess::ProfileThread(void *arg) 1581{ 1582 MachProcess *proc = (MachProcess*) arg; 1583 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg); 1584 1585 while (proc->IsProfilingEnabled()) 1586 { 1587 nub_state_t state = proc->GetState(); 1588 if (state == eStateRunning) 1589 { 1590 std::string data = proc->Task().GetProfileData(proc->GetProfileScanType()); 1591 if (!data.empty()) 1592 { 1593 proc->SignalAsyncProfileData(data.c_str()); 1594 } 1595 } 1596 else if ((state == eStateUnloaded) || (state == eStateDetached) || (state == eStateUnloaded)) 1597 { 1598 // Done. Get out of this thread. 1599 break; 1600 } 1601 1602 // A simple way to set up the profile interval. We can also use select() or dispatch timer source if necessary. 1603 usleep(proc->ProfileInterval()); 1604 } 1605 return NULL; 1606} 1607 1608 1609pid_t 1610MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len) 1611{ 1612 // Clear out and clean up from any current state 1613 Clear(); 1614 if (pid != 0) 1615 { 1616 DNBError err; 1617 // Make sure the process exists... 1618 if (::getpgid (pid) < 0) 1619 { 1620 err.SetErrorToErrno(); 1621 const char *err_cstr = err.AsString(); 1622 ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "No such process"); 1623 return INVALID_NUB_PROCESS; 1624 } 1625 1626 SetState(eStateAttaching); 1627 m_pid = pid; 1628 // Let ourselves know we are going to be using SBS or BKS if the correct flag bit is set... 1629#if defined (WITH_BKS) 1630 if (IsBKSProcess (pid)) 1631 m_flags |= eMachProcessFlagsUsingBKS; 1632#elif defined (WITH_SPRINGBOARD) 1633 if (IsSBProcess(pid)) 1634 m_flags |= eMachProcessFlagsUsingSBS; 1635#endif 1636 if (!m_task.StartExceptionThread(err)) 1637 { 1638 const char *err_cstr = err.AsString(); 1639 ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread"); 1640 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); 1641 m_pid = INVALID_NUB_PROCESS; 1642 return INVALID_NUB_PROCESS; 1643 } 1644 1645 errno = 0; 1646 if (::ptrace (PT_ATTACHEXC, pid, 0, 0)) 1647 err.SetError(errno); 1648 else 1649 err.Clear(); 1650 1651 if (err.Success()) 1652 { 1653 m_flags |= eMachProcessFlagsAttached; 1654 // Sleep a bit to let the exception get received and set our process status 1655 // to stopped. 1656 ::usleep(250000); 1657 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid); 1658 return m_pid; 1659 } 1660 else 1661 { 1662 ::snprintf (err_str, err_len, "%s", err.AsString()); 1663 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); 1664 } 1665 } 1666 return INVALID_NUB_PROCESS; 1667} 1668 1669// Do the process specific setup for attach. If this returns NULL, then there's no 1670// platform specific stuff to be done to wait for the attach. If you get non-null, 1671// pass that token to the CheckForProcess method, and then to CleanupAfterAttach. 1672 1673// Call PrepareForAttach before attaching to a process that has not yet launched 1674// This returns a token that can be passed to CheckForProcess, and to CleanupAfterAttach. 1675// You should call CleanupAfterAttach to free the token, and do whatever other 1676// cleanup seems good. 1677 1678const void * 1679MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &attach_err) 1680{ 1681#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) 1682 // Tell SpringBoard to halt the next launch of this application on startup. 1683 1684 if (!waitfor) 1685 return NULL; 1686 1687 const char *app_ext = strstr(path, ".app"); 1688 const bool is_app = app_ext != NULL && (app_ext[4] == '\0' || app_ext[4] == '/'); 1689 if (!is_app) 1690 { 1691 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrepareForAttach(): path '%s' doesn't contain .app, " 1692 "we can't tell springboard to wait for launch...", 1693 path); 1694 return NULL; 1695 } 1696 1697#if defined (WITH_BKS) 1698 if (launch_flavor == eLaunchFlavorDefault) 1699 launch_flavor = eLaunchFlavorBKS; 1700 if (launch_flavor != eLaunchFlavorBKS) 1701 return NULL; 1702#elif defined (WITH_SPRINGBOARD) 1703 if (launch_flavor == eLaunchFlavorDefault) 1704 launch_flavor = eLaunchFlavorSpringBoard; 1705 if (launch_flavor != eLaunchFlavorSpringBoard) 1706 return NULL; 1707#endif 1708 1709 std::string app_bundle_path(path, app_ext + strlen(".app")); 1710 1711 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), attach_err); 1712 std::string bundleIDStr; 1713 CFString::UTF8(bundleIDCFStr, bundleIDStr); 1714 DNBLogThreadedIf(LOG_PROCESS, 1715 "CopyBundleIDForPath (%s, err_str) returned @\"%s\"", 1716 app_bundle_path.c_str (), 1717 bundleIDStr.c_str()); 1718 1719 if (bundleIDCFStr == NULL) 1720 { 1721 return NULL; 1722 } 1723 1724#if defined (WITH_BKS) 1725 if (launch_flavor == eLaunchFlavorBKS) 1726 { 1727 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1728 1729 NSString *stdio_path = nil; 1730 NSFileManager *file_manager = [NSFileManager defaultManager]; 1731 const char *null_path = "/dev/null"; 1732 stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)]; 1733 1734 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 1735 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 1736 1737 DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: @\"%s\",options include stdio path: \"%s\", " 1738 "BKSDebugOptionKeyDebugOnNextLaunch & BKSDebugOptionKeyWaitForDebugger )", 1739 bundleIDStr.c_str(), 1740 null_path); 1741 1742 [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath]; 1743 [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath]; 1744 [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger]; 1745 [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDebugOnNextLaunch]; 1746 1747 [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; 1748 1749 BKSSystemService *system_service = [[BKSSystemService alloc] init]; 1750 1751 mach_port_t client_port = [system_service createClientPort]; 1752 __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 1753 __block BKSOpenApplicationErrorCode attach_error_code = BKSOpenApplicationErrorCodeNone; 1754 1755 NSString *bundleIDNSStr = (NSString *) bundleIDCFStr; 1756 1757 [system_service openApplication: bundleIDNSStr 1758 options: options 1759 clientPort: client_port 1760 withResult: ^(NSError *error) 1761 { 1762 // The system service will cleanup the client port we created for us. 1763 if (error) 1764 attach_error_code = (BKSOpenApplicationErrorCode)[error code]; 1765 1766 [system_service release]; 1767 dispatch_semaphore_signal(semaphore); 1768 } 1769 ]; 1770 1771 const uint32_t timeout_secs = 9; 1772 1773 dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); 1774 1775 long success = dispatch_semaphore_wait(semaphore, timeout) == 0; 1776 1777 if (!success) 1778 { 1779 DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str()); 1780 attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete."); 1781 attach_err.SetError (BKS_OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); 1782 } 1783 else if (attach_error_code != BKSOpenApplicationErrorCodeNone) 1784 { 1785 SetBKSError (attach_error_code, attach_err); 1786 DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", 1787 bundleIDStr.c_str(), 1788 attach_error_code); 1789 } 1790 dispatch_release(semaphore); 1791 [pool drain]; 1792 } 1793#elif defined (WITH_SPRINGBOARD) 1794 if (launch_flavor == eLaunchFlavorSpringBoard) 1795 { 1796 SBSApplicationLaunchError sbs_error = 0; 1797 1798 const char *stdout_err = "/dev/null"; 1799 CFString stdio_path; 1800 stdio_path.SetFileSystemRepresentation (stdout_err); 1801 1802 DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" , NULL, NULL, NULL, @\"%s\", @\"%s\", " 1803 "SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger )", 1804 bundleIDStr.c_str(), 1805 stdout_err, 1806 stdout_err); 1807 1808 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 1809 (CFURLRef)NULL, // openURL 1810 NULL, // launch_argv.get(), 1811 NULL, // launch_envp.get(), // CFDictionaryRef environment 1812 stdio_path.get(), 1813 stdio_path.get(), 1814 SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger); 1815 1816 if (sbs_error != SBSApplicationLaunchErrorSuccess) 1817 { 1818 attach_err.SetError(sbs_error, DNBError::SpringBoard); 1819 return NULL; 1820 } 1821 } 1822#endif // WITH_SPRINGBOARD 1823 1824 DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch."); 1825 return bundleIDCFStr; 1826# else // defined (WITH_SPRINGBOARD) || defined (WITH_BKS) 1827 return NULL; 1828#endif 1829} 1830 1831// Pass in the token you got from PrepareForAttach. If there is a process 1832// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS 1833// will be returned. 1834 1835nub_process_t 1836MachProcess::CheckForProcess (const void *attach_token) 1837{ 1838 if (attach_token == NULL) 1839 return INVALID_NUB_PROCESS; 1840 1841#if defined (WITH_BKS) 1842 NSString *bundleIDNSStr = (NSString *) attach_token; 1843 BKSSystemService *systemService = [[BKSSystemService alloc] init]; 1844 pid_t pid = [systemService pidForApplication: bundleIDNSStr]; 1845 [systemService release]; 1846 if (pid == 0) 1847 return INVALID_NUB_PROCESS; 1848 else 1849 return pid; 1850#elif defined (WITH_SPRINGBOARD) 1851 CFStringRef bundleIDCFStr = (CFStringRef) attach_token; 1852 Boolean got_it; 1853 nub_process_t attach_pid; 1854 got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid); 1855 if (got_it) 1856 return attach_pid; 1857 else 1858 return INVALID_NUB_PROCESS; 1859#else 1860 return INVALID_NUB_PROCESS; 1861#endif 1862} 1863 1864// Call this to clean up after you have either attached or given up on the attach. 1865// Pass true for success if you have attached, false if you have not. 1866// The token will also be freed at this point, so you can't use it after calling 1867// this method. 1868 1869void 1870MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str) 1871{ 1872 if (attach_token == NULL) 1873 return; 1874 1875#if defined (WITH_BKS) 1876 1877 if (!success) 1878 { 1879 BKSCleanupAfterAttach (attach_token, err_str); 1880 } 1881 CFRelease((CFStringRef) attach_token); 1882 1883#elif defined (WITH_SPRINGBOARD) 1884 // Tell SpringBoard to cancel the debug on next launch of this application 1885 // if we failed to attach 1886 if (!success) 1887 { 1888 SBSApplicationLaunchError sbs_error = 0; 1889 CFStringRef bundleIDCFStr = (CFStringRef) attach_token; 1890 1891 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 1892 (CFURLRef)NULL, 1893 NULL, 1894 NULL, 1895 NULL, 1896 NULL, 1897 SBSApplicationCancelDebugOnNextLaunch); 1898 1899 if (sbs_error != SBSApplicationLaunchErrorSuccess) 1900 { 1901 err_str.SetError(sbs_error, DNBError::SpringBoard); 1902 return; 1903 } 1904 } 1905 1906 CFRelease((CFStringRef) attach_token); 1907#endif 1908} 1909 1910pid_t 1911MachProcess::LaunchForDebug 1912( 1913 const char *path, 1914 char const *argv[], 1915 char const *envp[], 1916 const char *working_directory, // NULL => dont' change, non-NULL => set working directory for inferior to this 1917 const char *stdin_path, 1918 const char *stdout_path, 1919 const char *stderr_path, 1920 bool no_stdio, 1921 nub_launch_flavor_t launch_flavor, 1922 int disable_aslr, 1923 const char *event_data, 1924 DNBError &launch_err 1925) 1926{ 1927 // Clear out and clean up from any current state 1928 Clear(); 1929 1930 DNBLogThreadedIf(LOG_PROCESS, "%s( path = '%s', argv = %p, envp = %p, launch_flavor = %u, disable_aslr = %d )", __FUNCTION__, path, argv, envp, launch_flavor, disable_aslr); 1931 1932 // Fork a child process for debugging 1933 SetState(eStateLaunching); 1934 1935 switch (launch_flavor) 1936 { 1937 case eLaunchFlavorForkExec: 1938 m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err); 1939 break; 1940#ifdef WITH_BKS 1941 case eLaunchFlavorBKS: 1942 { 1943 const char *app_ext = strstr(path, ".app"); 1944 if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) 1945 { 1946 std::string app_bundle_path(path, app_ext + strlen(".app")); 1947 if (BKSLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0) 1948 return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid. 1949 else 1950 break; // We tried a BKS launch, but didn't succeed lets get out 1951 } 1952 } 1953 // In case the executable name has a ".app" fragment which confuses our debugserver, 1954 // let's do an intentional fallthrough here... 1955 launch_flavor = eLaunchFlavorPosixSpawn; 1956#endif 1957#ifdef WITH_SPRINGBOARD 1958 1959 case eLaunchFlavorSpringBoard: 1960 { 1961 // .../whatever.app/whatever ? 1962 // Or .../com.apple.whatever.app/whatever -- be careful of ".app" in "com.apple.whatever" here 1963 const char *app_ext = strstr (path, ".app/"); 1964 if (app_ext == NULL) 1965 { 1966 // .../whatever.app ? 1967 int len = strlen (path); 1968 if (len > 5) 1969 { 1970 if (strcmp (path + len - 4, ".app") == 0) 1971 { 1972 app_ext = path + len - 4; 1973 } 1974 } 1975 } 1976 if (app_ext) 1977 { 1978 std::string app_bundle_path(path, app_ext + strlen(".app")); 1979 if (SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, launch_err) != 0) 1980 return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid. 1981 else 1982 break; // We tried a springboard launch, but didn't succeed lets get out 1983 } 1984 } 1985 // In case the executable name has a ".app" fragment which confuses our debugserver, 1986 // let's do an intentional fallthrough here... 1987 launch_flavor = eLaunchFlavorPosixSpawn; 1988 1989#endif 1990 1991 case eLaunchFlavorPosixSpawn: 1992 m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path, 1993 DNBArchProtocol::GetArchitecture (), 1994 argv, 1995 envp, 1996 working_directory, 1997 stdin_path, 1998 stdout_path, 1999 stderr_path, 2000 no_stdio, 2001 this, 2002 disable_aslr, 2003 launch_err); 2004 break; 2005 2006 default: 2007 // Invalid launch 2008 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 2009 return INVALID_NUB_PROCESS; 2010 } 2011 2012 if (m_pid == INVALID_NUB_PROCESS) 2013 { 2014 // If we don't have a valid process ID and no one has set the error, 2015 // then return a generic error 2016 if (launch_err.Success()) 2017 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 2018 } 2019 else 2020 { 2021 m_path = path; 2022 size_t i; 2023 char const *arg; 2024 for (i=0; (arg = argv[i]) != NULL; i++) 2025 m_args.push_back(arg); 2026 2027 m_task.StartExceptionThread(launch_err); 2028 if (launch_err.Fail()) 2029 { 2030 if (launch_err.AsString() == NULL) 2031 launch_err.SetErrorString("unable to start the exception thread"); 2032 DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting."); 2033 ::ptrace (PT_KILL, m_pid, 0, 0); 2034 m_pid = INVALID_NUB_PROCESS; 2035 return INVALID_NUB_PROCESS; 2036 } 2037 2038 StartSTDIOThread(); 2039 2040 if (launch_flavor == eLaunchFlavorPosixSpawn) 2041 { 2042 2043 SetState (eStateAttaching); 2044 errno = 0; 2045 int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0); 2046 if (err == 0) 2047 { 2048 m_flags |= eMachProcessFlagsAttached; 2049 DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid); 2050 launch_err.Clear(); 2051 } 2052 else 2053 { 2054 SetState (eStateExited); 2055 DNBError ptrace_err(errno, DNBError::POSIX); 2056 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to spawned pid %d (err = %i, errno = %i (%s))", m_pid, err, ptrace_err.Error(), ptrace_err.AsString()); 2057 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 2058 } 2059 } 2060 else 2061 { 2062 launch_err.Clear(); 2063 } 2064 } 2065 return m_pid; 2066} 2067 2068pid_t 2069MachProcess::PosixSpawnChildForPTraceDebugging 2070( 2071 const char *path, 2072 cpu_type_t cpu_type, 2073 char const *argv[], 2074 char const *envp[], 2075 const char *working_directory, 2076 const char *stdin_path, 2077 const char *stdout_path, 2078 const char *stderr_path, 2079 bool no_stdio, 2080 MachProcess* process, 2081 int disable_aslr, 2082 DNBError& err 2083) 2084{ 2085 posix_spawnattr_t attr; 2086 short flags; 2087 DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, working_dir=%s, stdin=%s, stdout=%s stderr=%s, no-stdio=%i)", 2088 __FUNCTION__, 2089 path, 2090 argv, 2091 envp, 2092 working_directory, 2093 stdin_path, 2094 stdout_path, 2095 stderr_path, 2096 no_stdio); 2097 2098 err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX); 2099 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2100 err.LogThreaded("::posix_spawnattr_init ( &attr )"); 2101 if (err.Fail()) 2102 return INVALID_NUB_PROCESS; 2103 2104 flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; 2105 if (disable_aslr) 2106 flags |= _POSIX_SPAWN_DISABLE_ASLR; 2107 2108 sigset_t no_signals; 2109 sigset_t all_signals; 2110 sigemptyset (&no_signals); 2111 sigfillset (&all_signals); 2112 ::posix_spawnattr_setsigmask(&attr, &no_signals); 2113 ::posix_spawnattr_setsigdefault(&attr, &all_signals); 2114 2115 err.SetError( ::posix_spawnattr_setflags (&attr, flags), DNBError::POSIX); 2116 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2117 err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )", flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR" : ""); 2118 if (err.Fail()) 2119 return INVALID_NUB_PROCESS; 2120 2121 // Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail 2122 // and we will fail to continue with our process... 2123 2124 // On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment.... 2125 2126#if !defined(__arm__) 2127 2128 // We don't need to do this for ARM, and we really shouldn't now that we 2129 // have multiple CPU subtypes and no posix_spawnattr call that allows us 2130 // to set which CPU subtype to launch... 2131 if (cpu_type != 0) 2132 { 2133 size_t ocount = 0; 2134 err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), DNBError::POSIX); 2135 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2136 err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %llu )", cpu_type, (uint64_t)ocount); 2137 2138 if (err.Fail() != 0 || ocount != 1) 2139 return INVALID_NUB_PROCESS; 2140 } 2141#endif 2142 2143 PseudoTerminal pty; 2144 2145 posix_spawn_file_actions_t file_actions; 2146 err.SetError( ::posix_spawn_file_actions_init (&file_actions), DNBError::POSIX); 2147 int file_actions_valid = err.Success(); 2148 if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS)) 2149 err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )"); 2150 int pty_error = -1; 2151 pid_t pid = INVALID_NUB_PROCESS; 2152 if (file_actions_valid) 2153 { 2154 if (stdin_path == NULL && stdout_path == NULL && stderr_path == NULL && !no_stdio) 2155 { 2156 pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); 2157 if (pty_error == PseudoTerminal::success) 2158 { 2159 stdin_path = stdout_path = stderr_path = pty.SlaveName(); 2160 } 2161 } 2162 2163 // if no_stdio or std paths not supplied, then route to "/dev/null". 2164 if (no_stdio || stdin_path == NULL || stdin_path[0] == '\0') 2165 stdin_path = "/dev/null"; 2166 if (no_stdio || stdout_path == NULL || stdout_path[0] == '\0') 2167 stdout_path = "/dev/null"; 2168 if (no_stdio || stderr_path == NULL || stderr_path[0] == '\0') 2169 stderr_path = "/dev/null"; 2170 2171 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, 2172 STDIN_FILENO, 2173 stdin_path, 2174 O_RDONLY | O_NOCTTY, 2175 0), 2176 DNBError::POSIX); 2177 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 2178 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDIN_FILENO, path='%s')", stdin_path); 2179 2180 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, 2181 STDOUT_FILENO, 2182 stdout_path, 2183 O_WRONLY | O_NOCTTY | O_CREAT, 2184 0640), 2185 DNBError::POSIX); 2186 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 2187 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDOUT_FILENO, path='%s')", stdout_path); 2188 2189 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, 2190 STDERR_FILENO, 2191 stderr_path, 2192 O_WRONLY | O_NOCTTY | O_CREAT, 2193 0640), 2194 DNBError::POSIX); 2195 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 2196 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDERR_FILENO, path='%s')", stderr_path); 2197 2198 // TODO: Verify if we can set the working directory back immediately 2199 // after the posix_spawnp call without creating a race condition??? 2200 if (working_directory) 2201 ::chdir (working_directory); 2202 2203 err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); 2204 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2205 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp); 2206 } 2207 else 2208 { 2209 // TODO: Verify if we can set the working directory back immediately 2210 // after the posix_spawnp call without creating a race condition??? 2211 if (working_directory) 2212 ::chdir (working_directory); 2213 2214 err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); 2215 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2216 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp); 2217 } 2218 2219 // We have seen some cases where posix_spawnp was returning a valid 2220 // looking pid even when an error was returned, so clear it out 2221 if (err.Fail()) 2222 pid = INVALID_NUB_PROCESS; 2223 2224 if (pty_error == 0) 2225 { 2226 if (process != NULL) 2227 { 2228 int master_fd = pty.ReleaseMasterFD(); 2229 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 2230 } 2231 } 2232 ::posix_spawnattr_destroy (&attr); 2233 2234 if (pid != INVALID_NUB_PROCESS) 2235 { 2236 cpu_type_t pid_cpu_type = MachProcess::GetCPUTypeForLocalProcess (pid); 2237 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( ) pid=%i, cpu_type=0x%8.8x", __FUNCTION__, pid, pid_cpu_type); 2238 if (pid_cpu_type) 2239 DNBArchProtocol::SetArchitecture (pid_cpu_type); 2240 } 2241 2242 if (file_actions_valid) 2243 { 2244 DNBError err2; 2245 err2.SetError( ::posix_spawn_file_actions_destroy (&file_actions), DNBError::POSIX); 2246 if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2247 err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )"); 2248 } 2249 2250 return pid; 2251} 2252 2253uint32_t 2254MachProcess::GetCPUTypeForLocalProcess (pid_t pid) 2255{ 2256 int mib[CTL_MAXNAME]={0,}; 2257 size_t len = CTL_MAXNAME; 2258 if (::sysctlnametomib("sysctl.proc_cputype", mib, &len)) 2259 return 0; 2260 2261 mib[len] = pid; 2262 len++; 2263 2264 cpu_type_t cpu; 2265 size_t cpu_len = sizeof(cpu); 2266 if (::sysctl (mib, len, &cpu, &cpu_len, 0, 0)) 2267 cpu = 0; 2268 return cpu; 2269} 2270 2271pid_t 2272MachProcess::ForkChildForPTraceDebugging 2273( 2274 const char *path, 2275 char const *argv[], 2276 char const *envp[], 2277 MachProcess* process, 2278 DNBError& launch_err 2279) 2280{ 2281 PseudoTerminal::Error pty_error = PseudoTerminal::success; 2282 2283 // Use a fork that ties the child process's stdin/out/err to a pseudo 2284 // terminal so we can read it in our MachProcess::STDIOThread 2285 // as unbuffered io. 2286 PseudoTerminal pty; 2287 pid_t pid = pty.Fork(pty_error); 2288 2289 if (pid < 0) 2290 { 2291 //-------------------------------------------------------------- 2292 // Error during fork. 2293 //-------------------------------------------------------------- 2294 return pid; 2295 } 2296 else if (pid == 0) 2297 { 2298 //-------------------------------------------------------------- 2299 // Child process 2300 //-------------------------------------------------------------- 2301 ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process 2302 ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions 2303 2304 // If our parent is setgid, lets make sure we don't inherit those 2305 // extra powers due to nepotism. 2306 if (::setgid (getgid ()) == 0) 2307 { 2308 2309 // Let the child have its own process group. We need to execute 2310 // this call in both the child and parent to avoid a race condition 2311 // between the two processes. 2312 ::setpgid (0, 0); // Set the child process group to match its pid 2313 2314 // Sleep a bit to before the exec call 2315 ::sleep (1); 2316 2317 // Turn this process into 2318 ::execv (path, (char * const *)argv); 2319 } 2320 // Exit with error code. Child process should have taken 2321 // over in above exec call and if the exec fails it will 2322 // exit the child process below. 2323 ::exit (127); 2324 } 2325 else 2326 { 2327 //-------------------------------------------------------------- 2328 // Parent process 2329 //-------------------------------------------------------------- 2330 // Let the child have its own process group. We need to execute 2331 // this call in both the child and parent to avoid a race condition 2332 // between the two processes. 2333 ::setpgid (pid, pid); // Set the child process group to match its pid 2334 2335 if (process != NULL) 2336 { 2337 // Release our master pty file descriptor so the pty class doesn't 2338 // close it and so we can continue to use it in our STDIO thread 2339 int master_fd = pty.ReleaseMasterFD(); 2340 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 2341 } 2342 } 2343 return pid; 2344} 2345 2346#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) 2347// This returns a CFRetained pointer to the Bundle ID for app_bundle_path, 2348// or NULL if there was some problem getting the bundle id. 2349static CFStringRef 2350CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str) 2351{ 2352 CFBundle bundle(app_bundle_path); 2353 CFStringRef bundleIDCFStr = bundle.GetIdentifier(); 2354 std::string bundleID; 2355 if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) 2356 { 2357 struct stat app_bundle_stat; 2358 char err_msg[PATH_MAX]; 2359 2360 if (::stat (app_bundle_path, &app_bundle_stat) < 0) 2361 { 2362 err_str.SetError(errno, DNBError::POSIX); 2363 snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path); 2364 err_str.SetErrorString(err_msg); 2365 DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg); 2366 } 2367 else 2368 { 2369 err_str.SetError(-1, DNBError::Generic); 2370 snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path); 2371 err_str.SetErrorString(err_msg); 2372 DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path); 2373 } 2374 return NULL; 2375 } 2376 2377 DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); 2378 CFRetain (bundleIDCFStr); 2379 2380 return bundleIDCFStr; 2381} 2382#endif // #if defined 9WITH_SPRINGBOARD) || defined (WITH_BKS) 2383#ifdef WITH_SPRINGBOARD 2384 2385pid_t 2386MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, DNBError &launch_err) 2387{ 2388 // Clear out and clean up from any current state 2389 Clear(); 2390 2391 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); 2392 2393 // Fork a child process for debugging 2394 SetState(eStateLaunching); 2395 m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, no_stdio, this, launch_err); 2396 if (m_pid != 0) 2397 { 2398 m_flags |= eMachProcessFlagsUsingSBS; 2399 m_path = path; 2400 size_t i; 2401 char const *arg; 2402 for (i=0; (arg = argv[i]) != NULL; i++) 2403 m_args.push_back(arg); 2404 m_task.StartExceptionThread(launch_err); 2405 2406 if (launch_err.Fail()) 2407 { 2408 if (launch_err.AsString() == NULL) 2409 launch_err.SetErrorString("unable to start the exception thread"); 2410 DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting."); 2411 ::ptrace (PT_KILL, m_pid, 0, 0); 2412 m_pid = INVALID_NUB_PROCESS; 2413 return INVALID_NUB_PROCESS; 2414 } 2415 2416 StartSTDIOThread(); 2417 SetState (eStateAttaching); 2418 int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0); 2419 if (err == 0) 2420 { 2421 m_flags |= eMachProcessFlagsAttached; 2422 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid); 2423 } 2424 else 2425 { 2426 SetState (eStateExited); 2427 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid); 2428 } 2429 } 2430 return m_pid; 2431} 2432 2433#include <servers/bootstrap.h> 2434 2435pid_t 2436MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, MachProcess* process, DNBError &launch_err) 2437{ 2438 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process); 2439 CFAllocatorRef alloc = kCFAllocatorDefault; 2440 2441 if (argv[0] == NULL) 2442 return INVALID_NUB_PROCESS; 2443 2444 size_t argc = 0; 2445 // Count the number of arguments 2446 while (argv[argc] != NULL) 2447 argc++; 2448 2449 // Enumerate the arguments 2450 size_t first_launch_arg_idx = 1; 2451 CFReleaser<CFMutableArrayRef> launch_argv; 2452 2453 if (argv[first_launch_arg_idx]) 2454 { 2455 size_t launch_argc = argc > 0 ? argc - 1 : 0; 2456 launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks)); 2457 size_t i; 2458 char const *arg; 2459 CFString launch_arg; 2460 for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) 2461 { 2462 launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8)); 2463 if (launch_arg.get() != NULL) 2464 CFArrayAppendValue(launch_argv.get(), launch_arg.get()); 2465 else 2466 break; 2467 } 2468 } 2469 2470 // Next fill in the arguments dictionary. Note, the envp array is of the form 2471 // Variable=value but SpringBoard wants a CF dictionary. So we have to convert 2472 // this here. 2473 2474 CFReleaser<CFMutableDictionaryRef> launch_envp; 2475 2476 if (envp[0]) 2477 { 2478 launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); 2479 const char *value; 2480 int name_len; 2481 CFString name_string, value_string; 2482 2483 for (int i = 0; envp[i] != NULL; i++) 2484 { 2485 value = strstr (envp[i], "="); 2486 2487 // If the name field is empty or there's no =, skip it. Somebody's messing with us. 2488 if (value == NULL || value == envp[i]) 2489 continue; 2490 2491 name_len = value - envp[i]; 2492 2493 // Now move value over the "=" 2494 value++; 2495 2496 name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false)); 2497 value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8)); 2498 CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get()); 2499 } 2500 } 2501 2502 CFString stdio_path; 2503 2504 PseudoTerminal pty; 2505 if (!no_stdio) 2506 { 2507 PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); 2508 if (pty_err == PseudoTerminal::success) 2509 { 2510 const char* slave_name = pty.SlaveName(); 2511 DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); 2512 if (slave_name && slave_name[0]) 2513 { 2514 ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); 2515 stdio_path.SetFileSystemRepresentation (slave_name); 2516 } 2517 } 2518 } 2519 2520 if (stdio_path.get() == NULL) 2521 { 2522 stdio_path.SetFileSystemRepresentation ("/dev/null"); 2523 } 2524 2525 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err); 2526 if (bundleIDCFStr == NULL) 2527 return INVALID_NUB_PROCESS; 2528 2529 // This is just for logging: 2530 std::string bundleID; 2531 CFString::UTF8(bundleIDCFStr, bundleID); 2532 2533 DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__); 2534 2535 // Find SpringBoard 2536 SBSApplicationLaunchError sbs_error = 0; 2537 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 2538 (CFURLRef)NULL, // openURL 2539 launch_argv.get(), 2540 launch_envp.get(), // CFDictionaryRef environment 2541 stdio_path.get(), 2542 stdio_path.get(), 2543 SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice); 2544 2545 2546 launch_err.SetError(sbs_error, DNBError::SpringBoard); 2547 2548 if (sbs_error == SBSApplicationLaunchErrorSuccess) 2549 { 2550 static const useconds_t pid_poll_interval = 200000; 2551 static const useconds_t pid_poll_timeout = 30000000; 2552 2553 useconds_t pid_poll_total = 0; 2554 2555 nub_process_t pid = INVALID_NUB_PROCESS; 2556 Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); 2557 // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired 2558 // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started 2559 // yet, or that it died very quickly (if you weren't using waitForDebugger). 2560 while (!pid_found && pid_poll_total < pid_poll_timeout) 2561 { 2562 usleep (pid_poll_interval); 2563 pid_poll_total += pid_poll_interval; 2564 DNBLogThreadedIf(LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str()); 2565 pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); 2566 } 2567 2568 CFRelease (bundleIDCFStr); 2569 if (pid_found) 2570 { 2571 if (process != NULL) 2572 { 2573 // Release our master pty file descriptor so the pty class doesn't 2574 // close it and so we can continue to use it in our STDIO thread 2575 int master_fd = pty.ReleaseMasterFD(); 2576 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 2577 } 2578 DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid); 2579 } 2580 else 2581 { 2582 DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str()); 2583 } 2584 return pid; 2585 } 2586 2587 DNBLogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error); 2588 return INVALID_NUB_PROCESS; 2589} 2590 2591#endif // #ifdef WITH_SPRINGBOARD 2592 2593#ifdef WITH_BKS 2594 2595 2596// This function runs the BKSSystemService method openApplication:options:clientPort:withResult, 2597// messaging the app passed in bundleIDNSStr. 2598// The function should be run inside of an NSAutoReleasePool. 2599// 2600// It will use the "options" dictionary passed in, and fill the error passed in if there is an error. 2601// If return_pid is not NULL, we'll fetch the pid that was made for the bundleID. 2602// If bundleIDNSStr is NULL, then the system application will be messaged. 2603 2604static bool 2605CallBKSSystemServiceOpenApplication (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid) 2606{ 2607 // Now make our systemService: 2608 BKSSystemService *system_service = [[BKSSystemService alloc] init]; 2609 2610 if (bundleIDNSStr == nil) 2611 { 2612 bundleIDNSStr = [system_service systemApplicationBundleIdentifier]; 2613 if (bundleIDNSStr == nil) 2614 { 2615 // Okay, no system app... 2616 error.SetErrorString("No system application to message."); 2617 return false; 2618 } 2619 } 2620 2621 mach_port_t client_port = [system_service createClientPort]; 2622 __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 2623 __block BKSOpenApplicationErrorCode open_app_error = BKSOpenApplicationErrorCodeNone; 2624 bool wants_pid = (return_pid != NULL); 2625 __block pid_t pid_in_block; 2626 2627 const char *cstr = [bundleIDNSStr UTF8String]; 2628 if (!cstr) 2629 cstr = "<Unknown Bundle ID>"; 2630 2631 DNBLog ("About to launch process for bundle ID: %s", cstr); 2632 [system_service openApplication: bundleIDNSStr 2633 options: options 2634 clientPort: client_port 2635 withResult: ^(NSError *bks_error) 2636 { 2637 // The system service will cleanup the client port we created for us. 2638 if (bks_error) 2639 open_app_error = (BKSOpenApplicationErrorCode)[bks_error code]; 2640 2641 if (open_app_error == BKSOpenApplicationErrorCodeNone) 2642 { 2643 if (wants_pid) 2644 { 2645 pid_in_block = [system_service pidForApplication: bundleIDNSStr]; 2646 DNBLog("In completion handler, got pid for bundle id, pid: %d.", pid_in_block); 2647 DNBLogThreadedIf(LOG_PROCESS, "In completion handler, got pid for bundle id, pid: %d.", pid_in_block); 2648 } 2649 else 2650 DNBLogThreadedIf (LOG_PROCESS, "In completion handler: success."); 2651 } 2652 else 2653 { 2654 const char *error_str = [[bks_error localizedDescription] UTF8String]; 2655 DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send event, got error \"%s\"(%d).", 2656 error_str ? error_str : "<unknown error>", 2657 open_app_error); 2658 // REMOVE ME 2659 DNBLogError ("In completion handler for send event, got error \"%s\"(%d).", 2660 error_str ? error_str : "<unknown error>", 2661 open_app_error); 2662 } 2663 2664 [system_service release]; 2665 dispatch_semaphore_signal(semaphore); 2666 } 2667 2668 ]; 2669 2670 const uint32_t timeout_secs = 9; 2671 2672 dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); 2673 2674 long success = dispatch_semaphore_wait(semaphore, timeout) == 0; 2675 2676 dispatch_release(semaphore); 2677 2678 if (!success) 2679 { 2680 DNBLogError("timed out trying to send openApplication to %s.", cstr); 2681 error.SetError (BKS_OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); 2682 error.SetErrorString ("timed out trying to launch app"); 2683 } 2684 else if (open_app_error != BKSOpenApplicationErrorCodeNone) 2685 { 2686 SetBKSError (open_app_error, error); 2687 DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", cstr, open_app_error); 2688 success = false; 2689 } 2690 else if (wants_pid) 2691 { 2692 *return_pid = pid_in_block; 2693 DNBLogThreadedIf (LOG_PROCESS, "Out of completion handler, pid from block %d and passing out: %d", pid_in_block, *return_pid); 2694 } 2695 2696 2697 return success; 2698} 2699 2700void 2701MachProcess::BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str) 2702{ 2703 bool success; 2704 2705 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 2706 2707 // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here: 2708 NSString *bundleIDNSStr = (NSString *) attach_token; 2709 2710 // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: 2711 2712 // First we have the debug sub-dictionary: 2713 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 2714 [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyCancelDebugOnNextLaunch]; 2715 2716 // That will go in the overall dictionary: 2717 2718 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 2719 [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; 2720 2721 success = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, err_str, NULL); 2722 2723 if (!success) 2724 { 2725 DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString()); 2726 } 2727 2728 [pool drain]; 2729} 2730 2731bool 2732AddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error) 2733{ 2734 if (strcmp (event_data, "BackgroundContentFetching") == 0) 2735 { 2736 DNBLog("Setting ActivateForEvent key in options dictionary."); 2737 NSDictionary *event_details = [NSDictionary dictionary]; 2738 NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:BKSActivateForEventOptionTypeBackgroundContentFetching]; 2739 [options setObject: event_dictionary forKey: BKSOpenApplicationOptionKeyActivateForEvent]; 2740 return true; 2741 } 2742 else 2743 { 2744 DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data); 2745 option_error.SetErrorString("Unrecognized event data."); 2746 return false; 2747 } 2748 2749} 2750 2751pid_t 2752MachProcess::BKSLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err) 2753{ 2754 // Clear out and clean up from any current state 2755 Clear(); 2756 2757 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); 2758 2759 // Fork a child process for debugging 2760 SetState(eStateLaunching); 2761 m_pid = BKSForkChildForPTraceDebugging(path, argv, envp, no_stdio, disable_aslr, event_data, launch_err); 2762 if (m_pid != 0) 2763 { 2764 m_flags |= eMachProcessFlagsUsingBKS; 2765 m_path = path; 2766 size_t i; 2767 char const *arg; 2768 for (i=0; (arg = argv[i]) != NULL; i++) 2769 m_args.push_back(arg); 2770 m_task.StartExceptionThread(launch_err); 2771 2772 if (launch_err.Fail()) 2773 { 2774 if (launch_err.AsString() == NULL) 2775 launch_err.SetErrorString("unable to start the exception thread"); 2776 DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting."); 2777 ::ptrace (PT_KILL, m_pid, 0, 0); 2778 m_pid = INVALID_NUB_PROCESS; 2779 return INVALID_NUB_PROCESS; 2780 } 2781 2782 StartSTDIOThread(); 2783 SetState (eStateAttaching); 2784 int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0); 2785 if (err == 0) 2786 { 2787 m_flags |= eMachProcessFlagsAttached; 2788 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid); 2789 } 2790 else 2791 { 2792 SetState (eStateExited); 2793 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid); 2794 } 2795 } 2796 return m_pid; 2797} 2798 2799pid_t 2800MachProcess::BKSForkChildForPTraceDebugging (const char *app_bundle_path, 2801 char const *argv[], 2802 char const *envp[], 2803 bool no_stdio, 2804 bool disable_aslr, 2805 const char *event_data, 2806 DNBError &launch_err) 2807{ 2808 if (argv[0] == NULL) 2809 return INVALID_NUB_PROCESS; 2810 2811 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, this); 2812 2813 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 2814 2815 size_t argc = 0; 2816 // Count the number of arguments 2817 while (argv[argc] != NULL) 2818 argc++; 2819 2820 // Enumerate the arguments 2821 size_t first_launch_arg_idx = 1; 2822 2823 NSMutableArray *launch_argv = nil; 2824 2825 if (argv[first_launch_arg_idx]) 2826 { 2827 size_t launch_argc = argc > 0 ? argc - 1 : 0; 2828 launch_argv = [NSMutableArray arrayWithCapacity: launch_argc]; 2829 size_t i; 2830 char const *arg; 2831 NSString *launch_arg; 2832 for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) 2833 { 2834 launch_arg = [NSString stringWithUTF8String: arg]; 2835 // FIXME: Should we silently eat an argument that we can't convert into a UTF8 string? 2836 if (launch_arg != nil) 2837 [launch_argv addObject: launch_arg]; 2838 else 2839 break; 2840 } 2841 } 2842 2843 NSMutableDictionary *launch_envp = nil; 2844 if (envp[0]) 2845 { 2846 launch_envp = [[NSMutableDictionary alloc] init]; 2847 const char *value; 2848 int name_len; 2849 NSString *name_string, *value_string; 2850 2851 for (int i = 0; envp[i] != NULL; i++) 2852 { 2853 value = strstr (envp[i], "="); 2854 2855 // If the name field is empty or there's no =, skip it. Somebody's messing with us. 2856 if (value == NULL || value == envp[i]) 2857 continue; 2858 2859 name_len = value - envp[i]; 2860 2861 // Now move value over the "=" 2862 value++; 2863 name_string = [[NSString alloc] initWithBytes: envp[i] length: name_len encoding: NSUTF8StringEncoding]; 2864 value_string = [NSString stringWithUTF8String: value]; 2865 [launch_envp setObject: value_string forKey: name_string]; 2866 } 2867 } 2868 2869 NSString *stdio_path = nil; 2870 NSFileManager *file_manager = [NSFileManager defaultManager]; 2871 2872 PseudoTerminal pty; 2873 if (!no_stdio) 2874 { 2875 PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); 2876 if (pty_err == PseudoTerminal::success) 2877 { 2878 const char* slave_name = pty.SlaveName(); 2879 DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); 2880 if (slave_name && slave_name[0]) 2881 { 2882 ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); 2883 stdio_path = [file_manager stringWithFileSystemRepresentation: slave_name length: strlen(slave_name)]; 2884 } 2885 } 2886 } 2887 2888 if (stdio_path == nil) 2889 { 2890 const char *null_path = "/dev/null"; 2891 stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)]; 2892 } 2893 2894 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err); 2895 if (bundleIDCFStr == NULL) 2896 { 2897 [pool drain]; 2898 return INVALID_NUB_PROCESS; 2899 } 2900 2901 // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here: 2902 NSString *bundleIDNSStr = (NSString *) bundleIDCFStr; 2903 2904 // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: 2905 2906 // First we have the debug sub-dictionary: 2907 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 2908 if (launch_argv != nil) 2909 [debug_options setObject: launch_argv forKey: BKSDebugOptionKeyArguments]; 2910 if (launch_envp != nil) 2911 [debug_options setObject: launch_envp forKey: BKSDebugOptionKeyEnvironment]; 2912 2913 [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath]; 2914 [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath]; 2915 [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger]; 2916 if (disable_aslr) 2917 [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDisableASLR]; 2918 2919 // That will go in the overall dictionary: 2920 2921 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 2922 [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; 2923 2924 // For now we only support one kind of event: the "fetch" event, which is indicated by the fact that its data 2925 // is an empty dictionary. 2926 if (event_data != NULL && *event_data != '\0') 2927 { 2928 if (!AddEventDataToOptions(options, event_data, launch_err)) 2929 { 2930 [pool drain]; 2931 return INVALID_NUB_PROCESS; 2932 } 2933 } 2934 2935 // And there are some other options at the top level in this dictionary: 2936 [options setObject: [NSNumber numberWithBool: YES] forKey: BKSOpenApplicationOptionKeyUnlockDevice]; 2937 2938 pid_t return_pid = INVALID_NUB_PROCESS; 2939 bool success = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, launch_err, &return_pid); 2940 2941 if (success) 2942 { 2943 int master_fd = pty.ReleaseMasterFD(); 2944 SetChildFileDescriptors(master_fd, master_fd, master_fd); 2945 CFString::UTF8(bundleIDCFStr, m_bundle_id); 2946 } 2947 2948 [pool drain]; 2949 2950 return return_pid; 2951} 2952 2953bool 2954MachProcess::BKSSendEvent (const char *event_data, DNBError &send_err) 2955{ 2956 bool return_value = true; 2957 2958 if (event_data == NULL || *event_data == '\0') 2959 { 2960 DNBLogError ("SendEvent called with NULL event data."); 2961 send_err.SetErrorString("SendEvent called with empty event data"); 2962 return false; 2963 } 2964 2965 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 2966 2967 if (strcmp (event_data, "BackgroundApplication") == 0) 2968 { 2969 // This is an event I cooked up. What you actually do is foreground the system app, so: 2970 return_value = CallBKSSystemServiceOpenApplication(nil, nil, send_err, NULL); 2971 if (!return_value) 2972 { 2973 DNBLogError ("Failed to background application, error: %s.", send_err.AsString()); 2974 } 2975 } 2976 else 2977 { 2978 if (m_bundle_id.empty()) 2979 { 2980 // See if we can figure out the bundle ID for this PID: 2981 2982 DNBLogError ("Tried to send event \"%s\" to a process that has no bundle ID.", event_data); 2983 return false; 2984 } 2985 2986 NSString *bundleIDNSStr = [NSString stringWithUTF8String:m_bundle_id.c_str()]; 2987 2988 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 2989 2990 if (!AddEventDataToOptions(options, event_data, send_err)) 2991 { 2992 [pool drain]; 2993 return false; 2994 } 2995 2996 2997 return_value = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, send_err, NULL); 2998 2999 if (!return_value) 3000 { 3001 DNBLogError ("Failed to send event: %s, error: %s.", event_data, send_err.AsString()); 3002 } 3003 } 3004 3005 [pool drain]; 3006 return return_value; 3007} 3008#endif // WITH_BKS 3009