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