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