MachProcess.cpp revision 95ec1688db0efe51509ad9356052f5004d8856e0
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 <mach/mach.h> 16#include <signal.h> 17#include <spawn.h> 18#include <sys/fcntl.h> 19#include <sys/types.h> 20#include <sys/ptrace.h> 21#include <sys/stat.h> 22#include <sys/sysctl.h> 23#include <unistd.h> 24#include "MacOSX/CFUtils.h" 25#include "SysSignal.h" 26 27#include <algorithm> 28#include <map> 29 30#include "DNBDataRef.h" 31#include "DNBLog.h" 32#include "DNBThreadResumeActions.h" 33#include "DNBTimer.h" 34#include "MachProcess.h" 35#include "PseudoTerminal.h" 36 37#include "CFBundle.h" 38#include "CFData.h" 39#include "CFString.h" 40 41static CFStringRef CopyBundleIDForPath (const char *app_buncle_path, DNBError &err_str); 42 43#ifdef WITH_SPRINGBOARD 44 45#include <CoreFoundation/CoreFoundation.h> 46#include <SpringBoardServices/SpringBoardServer.h> 47#include <SpringBoardServices/SBSWatchdogAssertion.h> 48 49static bool 50IsSBProcess (nub_process_t pid) 51{ 52 bool opt_runningApps = true; 53 bool opt_debuggable = false; 54 55 CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable)); 56 if (sbsAppIDs.get() != NULL) 57 { 58 CFIndex count = ::CFArrayGetCount (sbsAppIDs.get()); 59 CFIndex i = 0; 60 for (i = 0; i < count; i++) 61 { 62 CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i); 63 64 // Get the process id for the app (if there is one) 65 pid_t sbs_pid = INVALID_NUB_PROCESS; 66 if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE) 67 { 68 if (sbs_pid == pid) 69 return true; 70 } 71 } 72 } 73 return false; 74} 75 76#endif 77 78#if 0 79#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) 80#else 81#define DEBUG_LOG(fmt, ...) 82#endif 83 84#ifndef MACH_PROCESS_USE_POSIX_SPAWN 85#define MACH_PROCESS_USE_POSIX_SPAWN 1 86#endif 87 88#ifndef _POSIX_SPAWN_DISABLE_ASLR 89#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 90#endif 91 92MachProcess::MachProcess() : 93 m_pid (0), 94 m_cpu_type (0), 95 m_child_stdin (-1), 96 m_child_stdout (-1), 97 m_child_stderr (-1), 98 m_path (), 99 m_args (), 100 m_task (this), 101 m_flags (eMachProcessFlagsNone), 102 m_stdio_thread (0), 103 m_stdio_mutex (PTHREAD_MUTEX_RECURSIVE), 104 m_stdout_data (), 105 m_thread_actions (), 106 m_thread_list (), 107 m_exception_messages (), 108 m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE), 109 m_state (eStateUnloaded), 110 m_state_mutex (PTHREAD_MUTEX_RECURSIVE), 111 m_events (0, kAllEventsMask), 112 m_breakpoints (), 113 m_watchpoints (), 114 m_name_to_addr_callback(NULL), 115 m_name_to_addr_baton(NULL), 116 m_image_infos_callback(NULL), 117 m_image_infos_baton(NULL) 118{ 119 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); 120} 121 122MachProcess::~MachProcess() 123{ 124 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); 125 Clear(); 126} 127 128pid_t 129MachProcess::SetProcessID(pid_t pid) 130{ 131 // Free any previous process specific data or resources 132 Clear(); 133 // Set the current PID appropriately 134 if (pid == 0) 135 m_pid = ::getpid (); 136 else 137 m_pid = pid; 138 return m_pid; // Return actualy PID in case a zero pid was passed in 139} 140 141nub_state_t 142MachProcess::GetState() 143{ 144 // If any other threads access this we will need a mutex for it 145 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); 146 return m_state; 147} 148 149const char * 150MachProcess::ThreadGetName(nub_thread_t tid) 151{ 152 return m_thread_list.GetName(tid); 153} 154 155nub_state_t 156MachProcess::ThreadGetState(nub_thread_t tid) 157{ 158 return m_thread_list.GetState(tid); 159} 160 161 162nub_size_t 163MachProcess::GetNumThreads () const 164{ 165 return m_thread_list.NumThreads(); 166} 167 168nub_thread_t 169MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const 170{ 171 return m_thread_list.ThreadIDAtIndex(thread_idx); 172} 173 174nub_thread_t 175MachProcess::GetCurrentThread () 176{ 177 return m_thread_list.CurrentThreadID(); 178} 179 180nub_thread_t 181MachProcess::SetCurrentThread(nub_thread_t tid) 182{ 183 return m_thread_list.SetCurrentThread(tid); 184} 185 186bool 187MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const 188{ 189 return m_thread_list.GetThreadStoppedReason(tid, stop_info); 190} 191 192void 193MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const 194{ 195 return m_thread_list.DumpThreadStoppedReason(tid); 196} 197 198const char * 199MachProcess::GetThreadInfo(nub_thread_t tid) const 200{ 201 return m_thread_list.GetThreadInfo(tid); 202} 203 204uint32_t 205MachProcess::GetCPUType () 206{ 207 if (m_cpu_type == 0 && m_pid != 0) 208 m_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid); 209 return m_cpu_type; 210} 211 212const DNBRegisterSetInfo * 213MachProcess::GetRegisterSetInfo (nub_thread_t tid, nub_size_t *num_reg_sets) const 214{ 215 MachThreadSP thread_sp (m_thread_list.GetThreadByID (tid)); 216 if (thread_sp) 217 { 218 DNBArchProtocol *arch = thread_sp->GetArchProtocol(); 219 if (arch) 220 return arch->GetRegisterSetInfo (num_reg_sets); 221 } 222 *num_reg_sets = 0; 223 return NULL; 224} 225 226bool 227MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const 228{ 229 return m_thread_list.GetRegisterValue(tid, set, reg, value); 230} 231 232bool 233MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const 234{ 235 return m_thread_list.SetRegisterValue(tid, set, reg, value); 236} 237 238void 239MachProcess::SetState(nub_state_t new_state) 240{ 241 // If any other threads access this we will need a mutex for it 242 uint32_t event_mask = 0; 243 244 // Scope for mutex locker 245 { 246 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); 247 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState ( %s )", DNBStateAsString(new_state)); 248 249 const nub_state_t old_state = m_state; 250 251 if (old_state != new_state) 252 { 253 if (NUB_STATE_IS_STOPPED(new_state)) 254 event_mask = eEventProcessStoppedStateChanged; 255 else 256 event_mask = eEventProcessRunningStateChanged; 257 258 m_state = new_state; 259 if (new_state == eStateStopped) 260 m_stop_count++; 261 } 262 } 263 264 if (event_mask != 0) 265 { 266 m_events.SetEvents (event_mask); 267 268 // Wait for the event bit to reset if a reset ACK is requested 269 m_events.WaitForResetAck(event_mask); 270 } 271 272} 273 274void 275MachProcess::Clear() 276{ 277 // Clear any cached thread list while the pid and task are still valid 278 279 m_task.Clear(); 280 // Now clear out all member variables 281 m_pid = INVALID_NUB_PROCESS; 282 CloseChildFileDescriptors(); 283 m_path.clear(); 284 m_args.clear(); 285 SetState(eStateUnloaded); 286 m_flags = eMachProcessFlagsNone; 287 m_stop_count = 0; 288 m_thread_list.Clear(); 289 { 290 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 291 m_exception_messages.clear(); 292 } 293} 294 295 296bool 297MachProcess::StartSTDIOThread() 298{ 299 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); 300 // Create the thread that watches for the child STDIO 301 return ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this) == 0; 302} 303 304 305nub_addr_t 306MachProcess::LookupSymbol(const char *name, const char *shlib) 307{ 308 if (m_name_to_addr_callback != NULL && name && name[0]) 309 return m_name_to_addr_callback(ProcessID(), name, shlib, m_name_to_addr_baton); 310 return INVALID_NUB_ADDRESS; 311} 312 313bool 314MachProcess::Resume (const DNBThreadResumeActions& thread_actions) 315{ 316 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()"); 317 nub_state_t state = GetState(); 318 319 if (CanResume(state)) 320 { 321 m_thread_actions = thread_actions; 322 PrivateResume(); 323 return true; 324 } 325 else if (state == eStateRunning) 326 { 327 DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", m_task.TaskPort()); 328 return true; 329 } 330 DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", m_task.TaskPort()); 331 return false; 332} 333 334bool 335MachProcess::Kill (const struct timespec *timeout_abstime) 336{ 337 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()"); 338 nub_state_t state = DoSIGSTOP(true); 339 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state)); 340 errno = 0; 341 ::ptrace (PT_KILL, m_pid, 0, 0); 342 DNBError err; 343 err.SetErrorToErrno(); 344 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", m_pid, err.Error(), err.AsString()); 345 m_thread_actions = DNBThreadResumeActions (eStateRunning, 0); 346 PrivateResume (); 347 return true; 348} 349 350bool 351MachProcess::Signal (int signal, const struct timespec *timeout_abstime) 352{ 353 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p)", signal, timeout_abstime); 354 nub_state_t state = GetState(); 355 if (::kill (ProcessID(), signal) == 0) 356 { 357 // If we were running and we have a timeout, wait for the signal to stop 358 if (IsRunning(state) && timeout_abstime) 359 { 360 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime); 361 m_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime); 362 state = GetState(); 363 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state)); 364 return !IsRunning (state); 365 } 366 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", signal, timeout_abstime); 367 return true; 368 } 369 DNBError err(errno, DNBError::POSIX); 370 err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal); 371 return false; 372 373} 374 375nub_state_t 376MachProcess::DoSIGSTOP (bool clear_bps_and_wps, uint32_t *thread_idx_ptr) 377{ 378 nub_state_t state = GetState(); 379 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", DNBStateAsString (state)); 380 381 if (!IsRunning(state)) 382 { 383 if (clear_bps_and_wps) 384 { 385 DisableAllBreakpoints (true); 386 DisableAllWatchpoints (true); 387 clear_bps_and_wps = false; 388 } 389 390 // If we already have a thread stopped due to a SIGSTOP, we don't have 391 // to do anything... 392 uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP); 393 if (thread_idx_ptr) 394 *thread_idx_ptr = thread_idx; 395 if (thread_idx != UINT32_MAX) 396 return GetState(); 397 398 // No threads were stopped with a SIGSTOP, we need to run and halt the 399 // process with a signal 400 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state)); 401 m_thread_actions = DNBThreadResumeActions (eStateRunning, 0); 402 PrivateResume (); 403 404 // Reset the event that says we were indeed running 405 m_events.ResetEvents(eEventProcessRunningStateChanged); 406 state = GetState(); 407 } 408 409 // We need to be stopped in order to be able to detach, so we need 410 // to send ourselves a SIGSTOP 411 412 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", DNBStateAsString (state)); 413 struct timespec sigstop_timeout; 414 DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0); 415 Signal (SIGSTOP, &sigstop_timeout); 416 if (clear_bps_and_wps) 417 { 418 DisableAllBreakpoints (true); 419 DisableAllWatchpoints (true); 420 // The static analyzer complains about this, but just leave the following line in. 421 clear_bps_and_wps = false; 422 } 423 uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP); 424 if (thread_idx_ptr) 425 *thread_idx_ptr = thread_idx; 426 return GetState(); 427} 428 429bool 430MachProcess::Detach() 431{ 432 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()"); 433 434 uint32_t thread_idx = UINT32_MAX; 435 nub_state_t state = DoSIGSTOP(true, &thread_idx); 436 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state)); 437 438 { 439 m_thread_actions.Clear(); 440 DNBThreadResumeAction thread_action; 441 thread_action.tid = m_thread_list.ThreadIDAtIndex (thread_idx); 442 thread_action.state = eStateRunning; 443 thread_action.signal = -1; 444 thread_action.addr = INVALID_NUB_ADDRESS; 445 446 m_thread_actions.Append (thread_action); 447 m_thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0); 448 449 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 450 451 ReplyToAllExceptions (); 452 453 } 454 455 m_task.ShutDownExcecptionThread(); 456 457 // Detach from our process 458 errno = 0; 459 nub_process_t pid = m_pid; 460 int ret = ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); 461 DNBError err(errno, DNBError::POSIX); 462 if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0)) 463 err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); 464 465 // Resume our task 466 m_task.Resume(); 467 468 // NULL our task out as we have already retored all exception ports 469 m_task.Clear(); 470 471 // Clear out any notion of the process we once were 472 Clear(); 473 474 SetState(eStateDetached); 475 476 return true; 477} 478 479nub_size_t 480MachProcess::RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, uint8_t *buf) const 481{ 482 nub_size_t bytes_removed = 0; 483 const DNBBreakpoint *bp; 484 nub_addr_t intersect_addr; 485 nub_size_t intersect_size; 486 nub_size_t opcode_offset; 487 nub_size_t idx; 488 for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx) 489 { 490 if (bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset)) 491 { 492 assert(addr <= intersect_addr && intersect_addr < addr + size); 493 assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); 494 assert(opcode_offset + intersect_size <= bp->ByteSize()); 495 nub_size_t buf_offset = intersect_addr - addr; 496 ::memcpy(buf + buf_offset, bp->SavedOpcodeBytes() + opcode_offset, intersect_size); 497 } 498 } 499 return bytes_removed; 500} 501 502//---------------------------------------------------------------------- 503// ReadMemory from the MachProcess level will always remove any software 504// breakpoints from the memory buffer before returning. If you wish to 505// read memory and see those traps, read from the MachTask 506// (m_task.ReadMemory()) as that version will give you what is actually 507// in inferior memory. 508//---------------------------------------------------------------------- 509nub_size_t 510MachProcess::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf) 511{ 512 // We need to remove any current software traps (enabled software 513 // breakpoints) that we may have placed in our tasks memory. 514 515 // First just read the memory as is 516 nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf); 517 518 // Then place any opcodes that fall into this range back into the buffer 519 // before we return this to callers. 520 if (bytes_read > 0) 521 RemoveTrapsFromBuffer (addr, size, (uint8_t *)buf); 522 return bytes_read; 523} 524 525//---------------------------------------------------------------------- 526// WriteMemory from the MachProcess level will always write memory around 527// any software breakpoints. Any software breakpoints will have their 528// opcodes modified if they are enabled. Any memory that doesn't overlap 529// with software breakpoints will be written to. If you wish to write to 530// inferior memory without this interference, then write to the MachTask 531// (m_task.WriteMemory()) as that version will always modify inferior 532// memory. 533//---------------------------------------------------------------------- 534nub_size_t 535MachProcess::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf) 536{ 537 // We need to write any data that would go where any current software traps 538 // (enabled software breakpoints) any software traps (breakpoints) that we 539 // may have placed in our tasks memory. 540 541 std::map<nub_addr_t, DNBBreakpoint *> addr_to_bp_map; 542 DNBBreakpoint *bp; 543 nub_size_t idx; 544 for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx) 545 { 546 if (bp->IntersectsRange(addr, size, NULL, NULL, NULL)) 547 addr_to_bp_map[bp->Address()] = bp; 548 } 549 550 // If we don't have any software breakpoints that are in this buffer, then 551 // we can just write memory and be done with it. 552 if (addr_to_bp_map.empty()) 553 return m_task.WriteMemory(addr, size, buf); 554 555 // If we make it here, we have some breakpoints that overlap and we need 556 // to work around them. 557 558 nub_size_t bytes_written = 0; 559 nub_addr_t intersect_addr; 560 nub_size_t intersect_size; 561 nub_size_t opcode_offset; 562 const uint8_t *ubuf = (const uint8_t *)buf; 563 std::map<nub_addr_t, DNBBreakpoint *>::iterator pos, end = addr_to_bp_map.end(); 564 for (pos = addr_to_bp_map.begin(); pos != end; ++pos) 565 { 566 bp = pos->second; 567 568 assert(bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset)); 569 assert(addr <= intersect_addr && intersect_addr < addr + size); 570 assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); 571 assert(opcode_offset + intersect_size <= bp->ByteSize()); 572 573 // Check for bytes before this breakpoint 574 const nub_addr_t curr_addr = addr + bytes_written; 575 if (intersect_addr > curr_addr) 576 { 577 // There are some bytes before this breakpoint that we need to 578 // just write to memory 579 nub_size_t curr_size = intersect_addr - curr_addr; 580 nub_size_t curr_bytes_written = m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written); 581 bytes_written += curr_bytes_written; 582 if (curr_bytes_written != curr_size) 583 { 584 // We weren't able to write all of the requested bytes, we 585 // are done looping and will return the number of bytes that 586 // we have written so far. 587 break; 588 } 589 } 590 591 // Now write any bytes that would cover up any software breakpoints 592 // directly into the breakpoint opcode buffer 593 ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size); 594 bytes_written += intersect_size; 595 } 596 597 // Write any remaining bytes after the last breakpoint if we have any left 598 if (bytes_written < size) 599 bytes_written += m_task.WriteMemory(addr + bytes_written, size - bytes_written, ubuf + bytes_written); 600 601 return bytes_written; 602} 603 604void 605MachProcess::ReplyToAllExceptions () 606{ 607 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 608 if (m_exception_messages.empty() == false) 609 { 610 MachException::Message::iterator pos; 611 MachException::Message::iterator begin = m_exception_messages.begin(); 612 MachException::Message::iterator end = m_exception_messages.end(); 613 for (pos = begin; pos != end; ++pos) 614 { 615 DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %u...", (uint32_t)std::distance(begin, pos)); 616 int thread_reply_signal = 0; 617 618 const DNBThreadResumeAction *action = m_thread_actions.GetActionForThread (pos->state.thread_port, false); 619 620 if (action) 621 { 622 thread_reply_signal = action->signal; 623 if (thread_reply_signal) 624 m_thread_actions.SetSignalHandledForThread (pos->state.thread_port); 625 } 626 627 DNBError err (pos->Reply(this, thread_reply_signal)); 628 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 629 err.LogThreadedIfError("Error replying to exception"); 630 } 631 632 // Erase all exception message as we should have used and replied 633 // to them all already. 634 m_exception_messages.clear(); 635 } 636} 637void 638MachProcess::PrivateResume () 639{ 640 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 641 642 ReplyToAllExceptions (); 643// bool stepOverBreakInstruction = step; 644 645 // Let the thread prepare to resume and see if any threads want us to 646 // step over a breakpoint instruction (ProcessWillResume will modify 647 // the value of stepOverBreakInstruction). 648 m_thread_list.ProcessWillResume (this, m_thread_actions); 649 650 // Set our state accordingly 651 if (m_thread_actions.NumActionsWithState(eStateStepping)) 652 SetState (eStateStepping); 653 else 654 SetState (eStateRunning); 655 656 // Now resume our task. 657 m_task.Resume(); 658} 659 660nub_break_t 661MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware, thread_t tid) 662{ 663 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %zu, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, hardware, tid); 664 if (hardware && tid == INVALID_NUB_THREAD) 665 tid = GetCurrentThread(); 666 667 DNBBreakpoint bp(addr, length, tid, hardware); 668 nub_break_t breakID = m_breakpoints.Add(bp); 669 if (EnableBreakpoint(breakID)) 670 { 671 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %zu, tid = 0x%4.4x ) => %u", (uint64_t)addr, length, tid, breakID); 672 return breakID; 673 } 674 else 675 { 676 m_breakpoints.Remove(breakID); 677 } 678 // We failed to enable the breakpoint 679 return INVALID_NUB_BREAK_ID; 680} 681 682nub_watch_t 683MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware, thread_t tid) 684{ 685 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %zu, flags = 0x%8.8x, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, watch_flags, hardware, tid); 686 if (hardware && tid == INVALID_NUB_THREAD) 687 tid = GetCurrentThread(); 688 689 DNBBreakpoint watch(addr, length, tid, hardware); 690 watch.SetIsWatchpoint(watch_flags); 691 692 nub_watch_t watchID = m_watchpoints.Add(watch); 693 if (EnableWatchpoint(watchID)) 694 { 695 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %zu, tid = 0x%x) => %u", (uint64_t)addr, length, tid, watchID); 696 return watchID; 697 } 698 else 699 { 700 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %zu, tid = 0x%x) => FAILED (%u)", (uint64_t)addr, length, tid, watchID); 701 m_watchpoints.Remove(watchID); 702 } 703 // We failed to enable the watchpoint 704 return INVALID_NUB_BREAK_ID; 705} 706 707nub_size_t 708MachProcess::DisableAllBreakpoints(bool remove) 709{ 710 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); 711 DNBBreakpoint *bp; 712 nub_size_t disabled_count = 0; 713 nub_size_t idx = 0; 714 while ((bp = m_breakpoints.GetByIndex(idx)) != NULL) 715 { 716 bool success = DisableBreakpoint(bp->GetID(), remove); 717 718 if (success) 719 disabled_count++; 720 // If we failed to disable the breakpoint or we aren't removing the breakpoint 721 // increment the breakpoint index. Otherwise DisableBreakpoint will have removed 722 // the breakpoint at this index and we don't need to change it. 723 if ((success == false) || (remove == false)) 724 idx++; 725 } 726 return disabled_count; 727} 728 729nub_size_t 730MachProcess::DisableAllWatchpoints(bool remove) 731{ 732 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); 733 DNBBreakpoint *wp; 734 nub_size_t disabled_count = 0; 735 nub_size_t idx = 0; 736 while ((wp = m_watchpoints.GetByIndex(idx)) != NULL) 737 { 738 bool success = DisableWatchpoint(wp->GetID(), remove); 739 740 if (success) 741 disabled_count++; 742 // If we failed to disable the watchpoint or we aren't removing the watchpoint 743 // increment the watchpoint index. Otherwise DisableWatchpoint will have removed 744 // the watchpoint at this index and we don't need to change it. 745 if ((success == false) || (remove == false)) 746 idx++; 747 } 748 return disabled_count; 749} 750 751bool 752MachProcess::DisableBreakpoint(nub_break_t breakID, bool remove) 753{ 754 DNBBreakpoint *bp = m_breakpoints.FindByID (breakID); 755 if (bp) 756 { 757 nub_addr_t addr = bp->Address(); 758 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx", breakID, remove, (uint64_t)addr); 759 760 if (bp->IsHardware()) 761 { 762 bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint (bp); 763 764 if (hw_disable_result == true) 765 { 766 bp->SetEnabled(false); 767 // Let the thread list know that a breakpoint has been modified 768 if (remove) 769 { 770 m_thread_list.NotifyBreakpointChanged(bp); 771 m_breakpoints.Remove(breakID); 772 } 773 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", breakID, remove, (uint64_t)addr); 774 return true; 775 } 776 777 return false; 778 } 779 780 const nub_size_t break_op_size = bp->ByteSize(); 781 assert (break_op_size > 0); 782 const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (bp->ByteSize()); 783 if (break_op_size > 0) 784 { 785 // Clear a software breakoint instruction 786 uint8_t curr_break_op[break_op_size]; 787 bool break_op_found = false; 788 789 // Read the breakpoint opcode 790 if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == break_op_size) 791 { 792 bool verify = false; 793 if (bp->IsEnabled()) 794 { 795 // Make sure we have the a breakpoint opcode exists at this address 796 if (memcmp(curr_break_op, break_op, break_op_size) == 0) 797 { 798 break_op_found = true; 799 // We found a valid breakpoint opcode at this address, now restore 800 // the saved opcode. 801 if (m_task.WriteMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size) 802 { 803 verify = true; 804 } 805 else 806 { 807 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx memory write failed when restoring original opcode", breakID, remove, (uint64_t)addr); 808 } 809 } 810 else 811 { 812 DNBLogWarning("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx expected a breakpoint opcode but didn't find one.", breakID, remove, (uint64_t)addr); 813 // Set verify to true and so we can check if the original opcode has already been restored 814 verify = true; 815 } 816 } 817 else 818 { 819 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx is not enabled", breakID, remove, (uint64_t)addr); 820 // Set verify to true and so we can check if the original opcode is there 821 verify = true; 822 } 823 824 if (verify) 825 { 826 uint8_t verify_opcode[break_op_size]; 827 // Verify that our original opcode made it back to the inferior 828 if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == break_op_size) 829 { 830 // compare the memory we just read with the original opcode 831 if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 0) 832 { 833 // SUCCESS 834 bp->SetEnabled(false); 835 // Let the thread list know that a breakpoint has been modified 836 if (remove) 837 { 838 m_thread_list.NotifyBreakpointChanged(bp); 839 m_breakpoints.Remove(breakID); 840 } 841 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx => success", breakID, remove, (uint64_t)addr); 842 return true; 843 } 844 else 845 { 846 if (break_op_found) 847 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: failed to restore original opcode", breakID, remove, (uint64_t)addr); 848 else 849 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: opcode changed", breakID, remove, (uint64_t)addr); 850 } 851 } 852 else 853 { 854 DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable breakpoint 0x%8.8llx", (uint64_t)addr); 855 } 856 } 857 } 858 else 859 { 860 DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory at 0x%8.8llx", (uint64_t)addr); 861 } 862 } 863 } 864 else 865 { 866 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) invalid breakpoint ID", breakID, remove); 867 } 868 return false; 869} 870 871bool 872MachProcess::DisableWatchpoint(nub_watch_t watchID, bool remove) 873{ 874 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(watchID = %d, remove = %d)", __FUNCTION__, watchID, remove); 875 DNBBreakpoint *wp = m_watchpoints.FindByID (watchID); 876 if (wp) 877 { 878 nub_addr_t addr = wp->Address(); 879 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx", watchID, remove, (uint64_t)addr); 880 881 if (wp->IsHardware()) 882 { 883 bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint (wp); 884 885 if (hw_disable_result == true) 886 { 887 wp->SetEnabled(false); 888 if (remove) 889 m_watchpoints.Remove(watchID); 890 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", watchID, remove, (uint64_t)addr); 891 return true; 892 } 893 } 894 895 // TODO: clear software watchpoints if we implement them 896 } 897 else 898 { 899 DNBLogError("MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) invalid watchpoint ID", watchID, remove); 900 } 901 return false; 902} 903 904 905void 906MachProcess::DumpBreakpoint(nub_break_t breakID) const 907{ 908 DNBLogThreaded("MachProcess::DumpBreakpoint(breakID = %d)", breakID); 909 910 if (NUB_BREAK_ID_IS_VALID(breakID)) 911 { 912 const DNBBreakpoint *bp = m_breakpoints.FindByID(breakID); 913 if (bp) 914 bp->Dump(); 915 else 916 DNBLog("MachProcess::DumpBreakpoint(breakID = %d): invalid breakID", breakID); 917 } 918 else 919 { 920 m_breakpoints.Dump(); 921 } 922} 923 924void 925MachProcess::DumpWatchpoint(nub_watch_t watchID) const 926{ 927 DNBLogThreaded("MachProcess::DumpWatchpoint(watchID = %d)", watchID); 928 929 if (NUB_BREAK_ID_IS_VALID(watchID)) 930 { 931 const DNBBreakpoint *wp = m_watchpoints.FindByID(watchID); 932 if (wp) 933 wp->Dump(); 934 else 935 DNBLog("MachProcess::DumpWatchpoint(watchID = %d): invalid watchID", watchID); 936 } 937 else 938 { 939 m_watchpoints.Dump(); 940 } 941} 942 943bool 944MachProcess::EnableBreakpoint(nub_break_t breakID) 945{ 946 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d )", breakID); 947 DNBBreakpoint *bp = m_breakpoints.FindByID (breakID); 948 if (bp) 949 { 950 nub_addr_t addr = bp->Address(); 951 if (bp->IsEnabled()) 952 { 953 DNBLogWarning("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint already enabled.", breakID, (uint64_t)addr); 954 return true; 955 } 956 else 957 { 958 if (bp->HardwarePreferred()) 959 { 960 bp->SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp)); 961 if (bp->IsHardware()) 962 { 963 bp->SetEnabled(true); 964 return true; 965 } 966 } 967 968 const nub_size_t break_op_size = bp->ByteSize(); 969 assert (break_op_size != 0); 970 const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (break_op_size); 971 if (break_op_size > 0) 972 { 973 // Save the original opcode by reading it 974 if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size) 975 { 976 // Write a software breakpoint in place of the original opcode 977 if (m_task.WriteMemory(addr, break_op_size, break_op) == break_op_size) 978 { 979 uint8_t verify_break_op[4]; 980 if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == break_op_size) 981 { 982 if (memcmp(break_op, verify_break_op, break_op_size) == 0) 983 { 984 bp->SetEnabled(true); 985 // Let the thread list know that a breakpoint has been modified 986 m_thread_list.NotifyBreakpointChanged(bp); 987 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: SUCCESS.", breakID, (uint64_t)addr); 988 return true; 989 } 990 else 991 { 992 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint opcode verification failed.", breakID, (uint64_t)addr); 993 } 994 } 995 else 996 { 997 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory to verify breakpoint opcode.", breakID, (uint64_t)addr); 998 } 999 } 1000 else 1001 { 1002 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to write breakpoint opcode to memory.", breakID, (uint64_t)addr); 1003 } 1004 } 1005 else 1006 { 1007 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory at breakpoint address.", breakID, (uint64_t)addr); 1008 } 1009 } 1010 else 1011 { 1012 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) no software breakpoint opcode for current architecture.", breakID); 1013 } 1014 } 1015 } 1016 return false; 1017} 1018 1019bool 1020MachProcess::EnableWatchpoint(nub_watch_t watchID) 1021{ 1022 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(watchID = %d)", watchID); 1023 DNBBreakpoint *wp = m_watchpoints.FindByID (watchID); 1024 if (wp) 1025 { 1026 nub_addr_t addr = wp->Address(); 1027 if (wp->IsEnabled()) 1028 { 1029 DNBLogWarning("MachProcess::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr); 1030 return true; 1031 } 1032 else 1033 { 1034 // Currently only try and set hardware watchpoints. 1035 wp->SetHardwareIndex(m_thread_list.EnableHardwareWatchpoint(wp)); 1036 if (wp->IsHardware()) 1037 { 1038 wp->SetEnabled(true); 1039 return true; 1040 } 1041 // TODO: Add software watchpoints by doing page protection tricks. 1042 } 1043 } 1044 return false; 1045} 1046 1047// Called by the exception thread when an exception has been received from 1048// our process. The exception message is completely filled and the exception 1049// data has already been copied. 1050void 1051MachProcess::ExceptionMessageReceived (const MachException::Message& exceptionMessage) 1052{ 1053 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 1054 1055 if (m_exception_messages.empty()) 1056 m_task.Suspend(); 1057 1058 DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )"); 1059 1060 // Use a locker to automatically unlock our mutex in case of exceptions 1061 // Add the exception to our internal exception stack 1062 m_exception_messages.push_back(exceptionMessage); 1063} 1064 1065void 1066MachProcess::ExceptionMessageBundleComplete() 1067{ 1068 // We have a complete bundle of exceptions for our child process. 1069 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 1070 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %zu exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size()); 1071 if (!m_exception_messages.empty()) 1072 { 1073 // Let all threads recover from stopping and do any clean up based 1074 // on the previous thread state (if any). 1075 m_thread_list.ProcessDidStop(this); 1076 1077 // Let each thread know of any exceptions 1078 task_t task = m_task.TaskPort(); 1079 size_t i; 1080 for (i=0; i<m_exception_messages.size(); ++i) 1081 { 1082 // Let the thread list figure use the MachProcess to forward all exceptions 1083 // on down to each thread. 1084 if (m_exception_messages[i].state.task_port == task) 1085 m_thread_list.NotifyException(m_exception_messages[i].state); 1086 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 1087 m_exception_messages[i].Dump(); 1088 } 1089 1090 if (DNBLogCheckLogBit(LOG_THREAD)) 1091 m_thread_list.Dump(); 1092 1093 bool step_more = false; 1094 if (m_thread_list.ShouldStop(step_more)) 1095 { 1096 // Wait for the eEventProcessRunningStateChanged event to be reset 1097 // before changing state to stopped to avoid race condition with 1098 // very fast start/stops 1099 struct timespec timeout; 1100 //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000); // Wait for 250 ms 1101 DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms 1102 m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout); 1103 SetState(eStateStopped); 1104 } 1105 else 1106 { 1107 // Resume without checking our current state. 1108 PrivateResume (); 1109 } 1110 } 1111 else 1112 { 1113 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s empty exception messages bundle (%zu exceptions).", __PRETTY_FUNCTION__, m_exception_messages.size()); 1114 } 1115} 1116 1117nub_size_t 1118MachProcess::CopyImageInfos ( struct DNBExecutableImageInfo **image_infos, bool only_changed) 1119{ 1120 if (m_image_infos_callback != NULL) 1121 return m_image_infos_callback(ProcessID(), image_infos, only_changed, m_image_infos_baton); 1122 return 0; 1123} 1124 1125void 1126MachProcess::SharedLibrariesUpdated ( ) 1127{ 1128 uint32_t event_bits = eEventSharedLibsStateChange; 1129 // Set the shared library event bit to let clients know of shared library 1130 // changes 1131 m_events.SetEvents(event_bits); 1132 // Wait for the event bit to reset if a reset ACK is requested 1133 m_events.WaitForResetAck(event_bits); 1134} 1135 1136void 1137MachProcess::AppendSTDOUT (char* s, size_t len) 1138{ 1139 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%zu> %s) ...", __FUNCTION__, len, s); 1140 PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex); 1141 m_stdout_data.append(s, len); 1142 m_events.SetEvents(eEventStdioAvailable); 1143 1144 // Wait for the event bit to reset if a reset ACK is requested 1145 m_events.WaitForResetAck(eEventStdioAvailable); 1146} 1147 1148size_t 1149MachProcess::GetAvailableSTDOUT (char *buf, size_t buf_size) 1150{ 1151 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%zu]) ...", __FUNCTION__, buf, buf_size); 1152 PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex); 1153 size_t bytes_available = m_stdout_data.size(); 1154 if (bytes_available > 0) 1155 { 1156 if (bytes_available > buf_size) 1157 { 1158 memcpy(buf, m_stdout_data.data(), buf_size); 1159 m_stdout_data.erase(0, buf_size); 1160 bytes_available = buf_size; 1161 } 1162 else 1163 { 1164 memcpy(buf, m_stdout_data.data(), bytes_available); 1165 m_stdout_data.clear(); 1166 } 1167 } 1168 return bytes_available; 1169} 1170 1171nub_addr_t 1172MachProcess::GetDYLDAllImageInfosAddress () 1173{ 1174 DNBError err; 1175 return m_task.GetDYLDAllImageInfosAddress(err); 1176} 1177 1178size_t 1179MachProcess::GetAvailableSTDERR (char *buf, size_t buf_size) 1180{ 1181 return 0; 1182} 1183 1184void * 1185MachProcess::STDIOThread(void *arg) 1186{ 1187 MachProcess *proc = (MachProcess*) arg; 1188 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg); 1189 1190 // We start use a base and more options so we can control if we 1191 // are currently using a timeout on the mach_msg. We do this to get a 1192 // bunch of related exceptions on our exception port so we can process 1193 // then together. When we have multiple threads, we can get an exception 1194 // per thread and they will come in consecutively. The main thread loop 1195 // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT 1196 // flag set in the options, so we will wait forever for an exception on 1197 // our exception port. After we get one exception, we then will use the 1198 // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current 1199 // exceptions for our process. After we have received the last pending 1200 // exception, we will get a timeout which enables us to then notify 1201 // our main thread that we have an exception bundle avaiable. We then wait 1202 // for the main thread to tell this exception thread to start trying to get 1203 // exceptions messages again and we start again with a mach_msg read with 1204 // infinite timeout. 1205 DNBError err; 1206 int stdout_fd = proc->GetStdoutFileDescriptor(); 1207 int stderr_fd = proc->GetStderrFileDescriptor(); 1208 if (stdout_fd == stderr_fd) 1209 stderr_fd = -1; 1210 1211 while (stdout_fd >= 0 || stderr_fd >= 0) 1212 { 1213 ::pthread_testcancel (); 1214 1215 fd_set read_fds; 1216 FD_ZERO (&read_fds); 1217 if (stdout_fd >= 0) 1218 FD_SET (stdout_fd, &read_fds); 1219 if (stderr_fd >= 0) 1220 FD_SET (stderr_fd, &read_fds); 1221 int nfds = std::max<int>(stdout_fd, stderr_fd) + 1; 1222 1223 int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL); 1224 DNBLogThreadedIf(LOG_PROCESS, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); 1225 1226 if (num_set_fds < 0) 1227 { 1228 int select_errno = errno; 1229 if (DNBLogCheckLogBit(LOG_PROCESS)) 1230 { 1231 err.SetError (select_errno, DNBError::POSIX); 1232 err.LogThreadedIfError("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); 1233 } 1234 1235 switch (select_errno) 1236 { 1237 case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO 1238 break; 1239 case EBADF: // One of the descriptor sets specified an invalid descriptor. 1240 return NULL; 1241 break; 1242 case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred. 1243 case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. 1244 default: // Other unknown error 1245 break; 1246 } 1247 } 1248 else if (num_set_fds == 0) 1249 { 1250 } 1251 else 1252 { 1253 char s[1024]; 1254 s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination 1255 int bytes_read = 0; 1256 if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds)) 1257 { 1258 do 1259 { 1260 bytes_read = ::read (stdout_fd, s, sizeof(s)-1); 1261 if (bytes_read < 0) 1262 { 1263 int read_errno = errno; 1264 DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); 1265 } 1266 else if (bytes_read == 0) 1267 { 1268 // EOF... 1269 DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read); 1270 stdout_fd = -1; 1271 } 1272 else if (bytes_read > 0) 1273 { 1274 proc->AppendSTDOUT(s, bytes_read); 1275 } 1276 1277 } while (bytes_read > 0); 1278 } 1279 1280 if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds)) 1281 { 1282 do 1283 { 1284 bytes_read = ::read (stderr_fd, s, sizeof(s)-1); 1285 if (bytes_read < 0) 1286 { 1287 int read_errno = errno; 1288 DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); 1289 } 1290 else if (bytes_read == 0) 1291 { 1292 // EOF... 1293 DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read); 1294 stderr_fd = -1; 1295 } 1296 else if (bytes_read > 0) 1297 { 1298 proc->AppendSTDOUT(s, bytes_read); 1299 } 1300 1301 } while (bytes_read > 0); 1302 } 1303 } 1304 } 1305 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", __FUNCTION__, arg); 1306 return NULL; 1307} 1308 1309pid_t 1310MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len) 1311{ 1312 // Clear out and clean up from any current state 1313 Clear(); 1314 if (pid != 0) 1315 { 1316 DNBError err; 1317 // Make sure the process exists... 1318 if (::getpgid (pid) < 0) 1319 { 1320 err.SetErrorToErrno(); 1321 const char *err_cstr = err.AsString(); 1322 ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "No such process"); 1323 return INVALID_NUB_PROCESS; 1324 } 1325 1326 SetState(eStateAttaching); 1327 m_pid = pid; 1328 // Let ourselves know we are going to be using SBS if the correct flag bit is set... 1329#ifdef WITH_SPRINGBOARD 1330 if (IsSBProcess(pid)) 1331 m_flags |= eMachProcessFlagsUsingSBS; 1332#endif 1333 if (!m_task.StartExceptionThread(err)) 1334 { 1335 const char *err_cstr = err.AsString(); 1336 ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread"); 1337 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); 1338 m_pid = INVALID_NUB_PROCESS; 1339 return INVALID_NUB_PROCESS; 1340 } 1341 1342 errno = 0; 1343 if (::ptrace (PT_ATTACHEXC, pid, 0, 0)) 1344 err.SetError(errno); 1345 else 1346 err.Clear(); 1347 1348 if (err.Success()) 1349 { 1350 m_flags |= eMachProcessFlagsAttached; 1351 // Sleep a bit to let the exception get received and set our process status 1352 // to stopped. 1353 ::usleep(250000); 1354 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid); 1355 return m_pid; 1356 } 1357 else 1358 { 1359 ::snprintf (err_str, err_len, "%s", err.AsString()); 1360 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); 1361 } 1362 } 1363 return INVALID_NUB_PROCESS; 1364} 1365 1366// Do the process specific setup for attach. If this returns NULL, then there's no 1367// platform specific stuff to be done to wait for the attach. If you get non-null, 1368// pass that token to the CheckForProcess method, and then to CleanupAfterAttach. 1369 1370// Call PrepareForAttach before attaching to a process that has not yet launched 1371// This returns a token that can be passed to CheckForProcess, and to CleanupAfterAttach. 1372// You should call CleanupAfterAttach to free the token, and do whatever other 1373// cleanup seems good. 1374 1375const void * 1376MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &err_str) 1377{ 1378#ifdef WITH_SPRINGBOARD 1379 // Tell SpringBoard to halt the next launch of this application on startup. 1380 1381 if (!waitfor) 1382 return NULL; 1383 1384 const char *app_ext = strstr(path, ".app"); 1385 if (app_ext == NULL) 1386 { 1387 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrepareForAttach(): path '%s' doesn't contain .app, we can't tell springboard to wait for launch...", path); 1388 return NULL; 1389 } 1390 1391 if (launch_flavor != eLaunchFlavorSpringBoard 1392 && launch_flavor != eLaunchFlavorDefault) 1393 return NULL; 1394 1395 std::string app_bundle_path(path, app_ext + strlen(".app")); 1396 1397 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), err_str); 1398 std::string bundleIDStr; 1399 CFString::UTF8(bundleIDCFStr, bundleIDStr); 1400 DNBLogThreadedIf(LOG_PROCESS, "CopyBundleIDForPath (%s, err_str) returned @\"%s\"", app_bundle_path.c_str (), bundleIDStr.c_str()); 1401 1402 if (bundleIDCFStr == NULL) 1403 { 1404 return NULL; 1405 } 1406 1407 SBSApplicationLaunchError sbs_error = 0; 1408 1409 const char *stdout_err = "/dev/null"; 1410 CFString stdio_path; 1411 stdio_path.SetFileSystemRepresentation (stdout_err); 1412 1413 DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" , NULL, NULL, NULL, @\"%s\", @\"%s\", SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger )", bundleIDStr.c_str(), stdout_err, stdout_err); 1414 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 1415 (CFURLRef)NULL, // openURL 1416 NULL, // launch_argv.get(), 1417 NULL, // launch_envp.get(), // CFDictionaryRef environment 1418 stdio_path.get(), 1419 stdio_path.get(), 1420 SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger); 1421 1422 if (sbs_error != SBSApplicationLaunchErrorSuccess) 1423 { 1424 err_str.SetError(sbs_error, DNBError::SpringBoard); 1425 return NULL; 1426 } 1427 1428 DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch."); 1429 return bundleIDCFStr; 1430# else 1431 return NULL; 1432#endif 1433} 1434 1435// Pass in the token you got from PrepareForAttach. If there is a process 1436// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS 1437// will be returned. 1438 1439nub_process_t 1440MachProcess::CheckForProcess (const void *attach_token) 1441{ 1442 if (attach_token == NULL) 1443 return INVALID_NUB_PROCESS; 1444 1445#ifdef WITH_SPRINGBOARD 1446 CFStringRef bundleIDCFStr = (CFStringRef) attach_token; 1447 Boolean got_it; 1448 nub_process_t attach_pid; 1449 got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid); 1450 if (got_it) 1451 return attach_pid; 1452 else 1453 return INVALID_NUB_PROCESS; 1454#endif 1455 return INVALID_NUB_PROCESS; 1456} 1457 1458// Call this to clean up after you have either attached or given up on the attach. 1459// Pass true for success if you have attached, false if you have not. 1460// The token will also be freed at this point, so you can't use it after calling 1461// this method. 1462 1463void 1464MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str) 1465{ 1466#ifdef WITH_SPRINGBOARD 1467 if (attach_token == NULL) 1468 return; 1469 1470 // Tell SpringBoard to cancel the debug on next launch of this application 1471 // if we failed to attach 1472 if (!success) 1473 { 1474 SBSApplicationLaunchError sbs_error = 0; 1475 CFStringRef bundleIDCFStr = (CFStringRef) attach_token; 1476 1477 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 1478 (CFURLRef)NULL, 1479 NULL, 1480 NULL, 1481 NULL, 1482 NULL, 1483 SBSApplicationCancelDebugOnNextLaunch); 1484 1485 if (sbs_error != SBSApplicationLaunchErrorSuccess) 1486 { 1487 err_str.SetError(sbs_error, DNBError::SpringBoard); 1488 return; 1489 } 1490 } 1491 1492 CFRelease((CFStringRef) attach_token); 1493#endif 1494} 1495 1496pid_t 1497MachProcess::LaunchForDebug 1498( 1499 const char *path, 1500 char const *argv[], 1501 char const *envp[], 1502 const char *working_directory, // NULL => dont' change, non-NULL => set working directory for inferior to this 1503 const char *stdin_path, 1504 const char *stdout_path, 1505 const char *stderr_path, 1506 bool no_stdio, 1507 nub_launch_flavor_t launch_flavor, 1508 int disable_aslr, 1509 DNBError &launch_err 1510) 1511{ 1512 // Clear out and clean up from any current state 1513 Clear(); 1514 1515 DNBLogThreadedIf(LOG_PROCESS, "%s( path = '%s', argv = %p, envp = %p, launch_flavor = %u, disable_aslr = %d )", __FUNCTION__, path, argv, envp, launch_flavor, disable_aslr); 1516 1517 // Fork a child process for debugging 1518 SetState(eStateLaunching); 1519 1520 switch (launch_flavor) 1521 { 1522 case eLaunchFlavorForkExec: 1523 m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err); 1524 break; 1525 1526 case eLaunchFlavorPosixSpawn: 1527 m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path, 1528 DNBArchProtocol::GetArchitecture (), 1529 argv, 1530 envp, 1531 working_directory, 1532 stdin_path, 1533 stdout_path, 1534 stderr_path, 1535 no_stdio, 1536 this, 1537 disable_aslr, 1538 launch_err); 1539 break; 1540 1541#ifdef WITH_SPRINGBOARD 1542 1543 case eLaunchFlavorSpringBoard: 1544 { 1545 const char *app_ext = strstr(path, ".app"); 1546 if (app_ext != NULL) 1547 { 1548 std::string app_bundle_path(path, app_ext + strlen(".app")); 1549 return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, launch_err); 1550 } 1551 } 1552 break; 1553 1554#endif 1555 1556 default: 1557 // Invalid launch 1558 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 1559 return INVALID_NUB_PROCESS; 1560 } 1561 1562 if (m_pid == INVALID_NUB_PROCESS) 1563 { 1564 // If we don't have a valid process ID and no one has set the error, 1565 // then return a generic error 1566 if (launch_err.Success()) 1567 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 1568 } 1569 else 1570 { 1571 m_path = path; 1572 size_t i; 1573 char const *arg; 1574 for (i=0; (arg = argv[i]) != NULL; i++) 1575 m_args.push_back(arg); 1576 1577 m_task.StartExceptionThread(launch_err); 1578 if (launch_err.Fail()) 1579 { 1580 if (launch_err.AsString() == NULL) 1581 launch_err.SetErrorString("unable to start the exception thread"); 1582 ::ptrace (PT_KILL, m_pid, 0, 0); 1583 m_pid = INVALID_NUB_PROCESS; 1584 return INVALID_NUB_PROCESS; 1585 } 1586 1587 StartSTDIOThread(); 1588 1589 if (launch_flavor == eLaunchFlavorPosixSpawn) 1590 { 1591 1592 SetState (eStateAttaching); 1593 errno = 0; 1594 int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0); 1595 if (err == 0) 1596 { 1597 m_flags |= eMachProcessFlagsAttached; 1598 DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid); 1599 launch_err.Clear(); 1600 } 1601 else 1602 { 1603 SetState (eStateExited); 1604 DNBError ptrace_err(errno, DNBError::POSIX); 1605 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()); 1606 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 1607 } 1608 } 1609 else 1610 { 1611 launch_err.Clear(); 1612 } 1613 } 1614 return m_pid; 1615} 1616 1617pid_t 1618MachProcess::PosixSpawnChildForPTraceDebugging 1619( 1620 const char *path, 1621 cpu_type_t cpu_type, 1622 char const *argv[], 1623 char const *envp[], 1624 const char *working_directory, 1625 const char *stdin_path, 1626 const char *stdout_path, 1627 const char *stderr_path, 1628 bool no_stdio, 1629 MachProcess* process, 1630 int disable_aslr, 1631 DNBError& err 1632) 1633{ 1634 posix_spawnattr_t attr; 1635 short flags; 1636 DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, working_dir=%s, stdin=%s, stdout=%s stderr=%s, no-stdio=%i)", 1637 __FUNCTION__, 1638 path, 1639 argv, 1640 envp, 1641 working_directory, 1642 stdin_path, 1643 stdout_path, 1644 stderr_path, 1645 no_stdio); 1646 1647 err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX); 1648 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1649 err.LogThreaded("::posix_spawnattr_init ( &attr )"); 1650 if (err.Fail()) 1651 return INVALID_NUB_PROCESS; 1652 1653 flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; 1654 if (disable_aslr) 1655 flags |= _POSIX_SPAWN_DISABLE_ASLR; 1656 1657 sigset_t no_signals; 1658 sigset_t all_signals; 1659 sigemptyset (&no_signals); 1660 sigfillset (&all_signals); 1661 ::posix_spawnattr_setsigmask(&attr, &no_signals); 1662 ::posix_spawnattr_setsigdefault(&attr, &all_signals); 1663 1664 err.SetError( ::posix_spawnattr_setflags (&attr, flags), DNBError::POSIX); 1665 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1666 err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )", flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR" : ""); 1667 if (err.Fail()) 1668 return INVALID_NUB_PROCESS; 1669 1670 // Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail 1671 // and we will fail to continue with our process... 1672 1673 // On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment.... 1674 1675#if !defined(__arm__) 1676 1677 // We don't need to do this for ARM, and we really shouldn't now that we 1678 // have multiple CPU subtypes and no posix_spawnattr call that allows us 1679 // to set which CPU subtype to launch... 1680 if (cpu_type != 0) 1681 { 1682 size_t ocount = 0; 1683 err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), DNBError::POSIX); 1684 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1685 err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu_type, ocount); 1686 1687 if (err.Fail() != 0 || ocount != 1) 1688 return INVALID_NUB_PROCESS; 1689 } 1690#endif 1691 1692 PseudoTerminal pty; 1693 1694 posix_spawn_file_actions_t file_actions; 1695 err.SetError( ::posix_spawn_file_actions_init (&file_actions), DNBError::POSIX); 1696 int file_actions_valid = err.Success(); 1697 if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS)) 1698 err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )"); 1699 int pty_error = -1; 1700 pid_t pid = INVALID_NUB_PROCESS; 1701 if (file_actions_valid) 1702 { 1703 if (stdin_path == NULL && stdout_path == NULL && stderr_path == NULL && !no_stdio) 1704 { 1705 pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); 1706 if (pty_error == PseudoTerminal::success) 1707 { 1708 stdin_path = stdout_path = stderr_path = pty.SlaveName(); 1709 } 1710 } 1711 1712 // if no_stdio, then do open file actions, opening /dev/null. 1713 if (no_stdio) 1714 { 1715 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, STDIN_FILENO, "/dev/null", 1716 O_RDONLY | O_NOCTTY, 0), DNBError::POSIX); 1717 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 1718 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDIN_FILENO, path=/dev/null)"); 1719 1720 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, STDOUT_FILENO, "/dev/null", 1721 O_WRONLY | O_NOCTTY, 0), DNBError::POSIX); 1722 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 1723 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDOUT_FILENO, path=/dev/null)"); 1724 1725 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, STDERR_FILENO, "/dev/null", 1726 O_WRONLY | O_NOCTTY, 0), DNBError::POSIX); 1727 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 1728 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDERR_FILENO, path=/dev/null)"); 1729 } 1730 else 1731 { 1732 if ( stdin_path == NULL) stdin_path = "/dev/null"; 1733 if (stdout_path == NULL) stdout_path = "/dev/null"; 1734 if (stderr_path == NULL) stderr_path = "/dev/null"; 1735 1736 const int slave_fd_in = open (stdin_path , O_NOCTTY | O_RDONLY); 1737 const int slave_fd_out = open (stdout_path, O_NOCTTY | O_CREAT | O_WRONLY , 0640); 1738 const int slave_fd_err = open (stderr_path, O_NOCTTY | O_CREAT | O_WRONLY , 0640); 1739 1740 err.SetError( ::posix_spawn_file_actions_adddup2(&file_actions, slave_fd_err, STDERR_FILENO), DNBError::POSIX); 1741 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1742 err.LogThreaded("::posix_spawn_file_actions_adddup2 ( &file_actions, filedes = %d (\"%s\"), newfiledes = STDERR_FILENO )", slave_fd_err, stderr_path); 1743 1744 err.SetError( ::posix_spawn_file_actions_adddup2(&file_actions, slave_fd_in, STDIN_FILENO), DNBError::POSIX); 1745 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1746 err.LogThreaded("::posix_spawn_file_actions_adddup2 ( &file_actions, filedes = %d (\"%s\"), newfiledes = STDIN_FILENO )", slave_fd_in, stdin_path); 1747 1748 err.SetError( ::posix_spawn_file_actions_adddup2(&file_actions, slave_fd_out, STDOUT_FILENO), DNBError::POSIX); 1749 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1750 err.LogThreaded("::posix_spawn_file_actions_adddup2 ( &file_actions, filedes = %d (\"%s\"), newfiledes = STDOUT_FILENO )", slave_fd_out, stdout_path); 1751 } 1752 1753 // TODO: Verify if we can set the working directory back immediately 1754 // after the posix_spawnp call without creating a race condition??? 1755 if (working_directory) 1756 ::chdir (working_directory); 1757 1758 err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); 1759 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1760 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp); 1761 } 1762 else 1763 { 1764 // TODO: Verify if we can set the working directory back immediately 1765 // after the posix_spawnp call without creating a race condition??? 1766 if (working_directory) 1767 ::chdir (working_directory); 1768 1769 err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); 1770 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1771 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp); 1772 } 1773 1774 // We have seen some cases where posix_spawnp was returning a valid 1775 // looking pid even when an error was returned, so clear it out 1776 if (err.Fail()) 1777 pid = INVALID_NUB_PROCESS; 1778 1779 if (pty_error == 0) 1780 { 1781 if (process != NULL) 1782 { 1783 int master_fd = pty.ReleaseMasterFD(); 1784 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 1785 } 1786 } 1787 ::posix_spawnattr_destroy (&attr); 1788 1789 if (pid != INVALID_NUB_PROCESS) 1790 { 1791 cpu_type_t pid_cpu_type = MachProcess::GetCPUTypeForLocalProcess (pid); 1792 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( ) pid=%i, cpu_type=0x%8.8x", __FUNCTION__, pid, pid_cpu_type); 1793 if (pid_cpu_type) 1794 DNBArchProtocol::SetArchitecture (pid_cpu_type); 1795 } 1796 1797 if (file_actions_valid) 1798 { 1799 DNBError err2; 1800 err2.SetError( ::posix_spawn_file_actions_destroy (&file_actions), DNBError::POSIX); 1801 if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1802 err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )"); 1803 } 1804 1805 return pid; 1806} 1807 1808uint32_t 1809MachProcess::GetCPUTypeForLocalProcess (pid_t pid) 1810{ 1811 int mib[CTL_MAXNAME]={0,}; 1812 size_t len = CTL_MAXNAME; 1813 if (::sysctlnametomib("sysctl.proc_cputype", mib, &len)) 1814 return 0; 1815 1816 mib[len] = pid; 1817 len++; 1818 1819 cpu_type_t cpu; 1820 size_t cpu_len = sizeof(cpu); 1821 if (::sysctl (mib, len, &cpu, &cpu_len, 0, 0)) 1822 cpu = 0; 1823 return cpu; 1824} 1825 1826pid_t 1827MachProcess::ForkChildForPTraceDebugging 1828( 1829 const char *path, 1830 char const *argv[], 1831 char const *envp[], 1832 MachProcess* process, 1833 DNBError& launch_err 1834) 1835{ 1836 PseudoTerminal::Error pty_error = PseudoTerminal::success; 1837 1838 // Use a fork that ties the child process's stdin/out/err to a pseudo 1839 // terminal so we can read it in our MachProcess::STDIOThread 1840 // as unbuffered io. 1841 PseudoTerminal pty; 1842 pid_t pid = pty.Fork(pty_error); 1843 1844 if (pid < 0) 1845 { 1846 //-------------------------------------------------------------- 1847 // Error during fork. 1848 //-------------------------------------------------------------- 1849 return pid; 1850 } 1851 else if (pid == 0) 1852 { 1853 //-------------------------------------------------------------- 1854 // Child process 1855 //-------------------------------------------------------------- 1856 ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process 1857 ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions 1858 1859 // If our parent is setgid, lets make sure we don't inherit those 1860 // extra powers due to nepotism. 1861 ::setgid (getgid ()); 1862 1863 // Let the child have its own process group. We need to execute 1864 // this call in both the child and parent to avoid a race condition 1865 // between the two processes. 1866 ::setpgid (0, 0); // Set the child process group to match its pid 1867 1868 // Sleep a bit to before the exec call 1869 ::sleep (1); 1870 1871 // Turn this process into 1872 ::execv (path, (char * const *)argv); 1873 // Exit with error code. Child process should have taken 1874 // over in above exec call and if the exec fails it will 1875 // exit the child process below. 1876 ::exit (127); 1877 } 1878 else 1879 { 1880 //-------------------------------------------------------------- 1881 // Parent process 1882 //-------------------------------------------------------------- 1883 // Let the child have its own process group. We need to execute 1884 // this call in both the child and parent to avoid a race condition 1885 // between the two processes. 1886 ::setpgid (pid, pid); // Set the child process group to match its pid 1887 1888 if (process != NULL) 1889 { 1890 // Release our master pty file descriptor so the pty class doesn't 1891 // close it and so we can continue to use it in our STDIO thread 1892 int master_fd = pty.ReleaseMasterFD(); 1893 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 1894 } 1895 } 1896 return pid; 1897} 1898 1899#ifdef WITH_SPRINGBOARD 1900 1901pid_t 1902MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, DNBError &launch_err) 1903{ 1904 // Clear out and clean up from any current state 1905 Clear(); 1906 1907 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); 1908 1909 // Fork a child process for debugging 1910 SetState(eStateLaunching); 1911 m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, no_stdio, this, launch_err); 1912 if (m_pid != 0) 1913 { 1914 m_flags |= eMachProcessFlagsUsingSBS; 1915 m_path = path; 1916 size_t i; 1917 char const *arg; 1918 for (i=0; (arg = argv[i]) != NULL; i++) 1919 m_args.push_back(arg); 1920 m_task.StartExceptionThread(launch_err); 1921 1922 if (launch_err.Fail()) 1923 { 1924 if (launch_err.AsString() == NULL) 1925 launch_err.SetErrorString("unable to start the exception thread"); 1926 ::ptrace (PT_KILL, m_pid, 0, 0); 1927 m_pid = INVALID_NUB_PROCESS; 1928 return INVALID_NUB_PROCESS; 1929 } 1930 1931 StartSTDIOThread(); 1932 SetState (eStateAttaching); 1933 int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0); 1934 if (err == 0) 1935 { 1936 m_flags |= eMachProcessFlagsAttached; 1937 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid); 1938 } 1939 else 1940 { 1941 SetState (eStateExited); 1942 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid); 1943 } 1944 } 1945 return m_pid; 1946} 1947 1948#include <servers/bootstrap.h> 1949 1950// This returns a CFRetained pointer to the Bundle ID for app_bundle_path, 1951// or NULL if there was some problem getting the bundle id. 1952static CFStringRef 1953CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str) 1954{ 1955 CFBundle bundle(app_bundle_path); 1956 CFStringRef bundleIDCFStr = bundle.GetIdentifier(); 1957 std::string bundleID; 1958 if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) 1959 { 1960 struct stat app_bundle_stat; 1961 char err_msg[PATH_MAX]; 1962 1963 if (::stat (app_bundle_path, &app_bundle_stat) < 0) 1964 { 1965 err_str.SetError(errno, DNBError::POSIX); 1966 snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path); 1967 err_str.SetErrorString(err_msg); 1968 DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg); 1969 } 1970 else 1971 { 1972 err_str.SetError(-1, DNBError::Generic); 1973 snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path); 1974 err_str.SetErrorString(err_msg); 1975 DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path); 1976 } 1977 return NULL; 1978 } 1979 1980 DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); 1981 CFRetain (bundleIDCFStr); 1982 1983 return bundleIDCFStr; 1984} 1985 1986pid_t 1987MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, MachProcess* process, DNBError &launch_err) 1988{ 1989 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process); 1990 CFAllocatorRef alloc = kCFAllocatorDefault; 1991 1992 if (argv[0] == NULL) 1993 return INVALID_NUB_PROCESS; 1994 1995 size_t argc = 0; 1996 // Count the number of arguments 1997 while (argv[argc] != NULL) 1998 argc++; 1999 2000 // Enumerate the arguments 2001 size_t first_launch_arg_idx = 1; 2002 CFReleaser<CFMutableArrayRef> launch_argv; 2003 2004 if (argv[first_launch_arg_idx]) 2005 { 2006 size_t launch_argc = argc > 0 ? argc - 1 : 0; 2007 launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks)); 2008 size_t i; 2009 char const *arg; 2010 CFString launch_arg; 2011 for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) 2012 { 2013 launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8)); 2014 if (launch_arg.get() != NULL) 2015 CFArrayAppendValue(launch_argv.get(), launch_arg.get()); 2016 else 2017 break; 2018 } 2019 } 2020 2021 // Next fill in the arguments dictionary. Note, the envp array is of the form 2022 // Variable=value but SpringBoard wants a CF dictionary. So we have to convert 2023 // this here. 2024 2025 CFReleaser<CFMutableDictionaryRef> launch_envp; 2026 2027 if (envp[0]) 2028 { 2029 launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); 2030 const char *value; 2031 int name_len; 2032 CFString name_string, value_string; 2033 2034 for (int i = 0; envp[i] != NULL; i++) 2035 { 2036 value = strstr (envp[i], "="); 2037 2038 // If the name field is empty or there's no =, skip it. Somebody's messing with us. 2039 if (value == NULL || value == envp[i]) 2040 continue; 2041 2042 name_len = value - envp[i]; 2043 2044 // Now move value over the "=" 2045 value++; 2046 2047 name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false)); 2048 value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8)); 2049 CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get()); 2050 } 2051 } 2052 2053 CFString stdio_path; 2054 2055 PseudoTerminal pty; 2056 if (!no_stdio) 2057 { 2058 PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); 2059 if (pty_err == PseudoTerminal::success) 2060 { 2061 const char* slave_name = pty.SlaveName(); 2062 DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); 2063 if (slave_name && slave_name[0]) 2064 { 2065 ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); 2066 stdio_path.SetFileSystemRepresentation (slave_name); 2067 } 2068 } 2069 } 2070 2071 if (stdio_path.get() == NULL) 2072 { 2073 stdio_path.SetFileSystemRepresentation ("/dev/null"); 2074 } 2075 2076 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err); 2077 if (bundleIDCFStr == NULL) 2078 return INVALID_NUB_PROCESS; 2079 2080 std::string bundleID; 2081 CFString::UTF8(bundleIDCFStr, bundleID); 2082 2083 CFData argv_data(NULL); 2084 2085 if (launch_argv.get()) 2086 { 2087 if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL) 2088 { 2089 DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__); 2090 return INVALID_NUB_PROCESS; 2091 } 2092 } 2093 2094 DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__); 2095 2096 // Find SpringBoard 2097 SBSApplicationLaunchError sbs_error = 0; 2098 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 2099 (CFURLRef)NULL, // openURL 2100 launch_argv.get(), 2101 launch_envp.get(), // CFDictionaryRef environment 2102 stdio_path.get(), 2103 stdio_path.get(), 2104 SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice); 2105 2106 2107 launch_err.SetError(sbs_error, DNBError::SpringBoard); 2108 2109 if (sbs_error == SBSApplicationLaunchErrorSuccess) 2110 { 2111 static const useconds_t pid_poll_interval = 200000; 2112 static const useconds_t pid_poll_timeout = 30000000; 2113 2114 useconds_t pid_poll_total = 0; 2115 2116 nub_process_t pid = INVALID_NUB_PROCESS; 2117 Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); 2118 // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired 2119 // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started 2120 // yet, or that it died very quickly (if you weren't using waitForDebugger). 2121 while (!pid_found && pid_poll_total < pid_poll_timeout) 2122 { 2123 usleep (pid_poll_interval); 2124 pid_poll_total += pid_poll_interval; 2125 DNBLogThreadedIf(LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str()); 2126 pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); 2127 } 2128 2129 CFRelease (bundleIDCFStr); 2130 if (pid_found) 2131 { 2132 if (process != NULL) 2133 { 2134 // Release our master pty file descriptor so the pty class doesn't 2135 // close it and so we can continue to use it in our STDIO thread 2136 int master_fd = pty.ReleaseMasterFD(); 2137 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 2138 } 2139 DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid); 2140 } 2141 else 2142 { 2143 DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str()); 2144 } 2145 return pid; 2146 } 2147 2148 DNBLogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error); 2149 return INVALID_NUB_PROCESS; 2150} 2151 2152#endif // #ifdef WITH_SPRINGBOARD 2153 2154 2155