bool MachThread::RestoreSuspendCount() { DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); DNBError err; if (ThreadIDIsValid(m_tid) == false) return false; else if (m_suspendCount > m_basicInfo.suspend_count) { while (m_suspendCount > m_basicInfo.suspend_count) { err = ::thread_resume (m_tid); if (err.Success()) --m_suspendCount; if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) err.LogThreaded("::thread_resume (%4.4x)", m_tid); } } else if (m_suspendCount < m_basicInfo.suspend_count) { while (m_suspendCount < m_basicInfo.suspend_count) { err = ::thread_suspend (m_tid); if (err.Success()) --m_suspendCount; if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) err.LogThreaded("::thread_suspend (%4.4x)", m_tid); } } return m_suspendCount == m_basicInfo.suspend_count; }
kern_return_t MachTask::ShutDownExcecptionThread() { DNBError err; err = RestoreExceptionPortInfo(); // NULL our our exception port and let our exception thread exit mach_port_t exception_port = m_exception_port; m_exception_port = NULL; err.SetError(::pthread_cancel(m_exception_thread), DNBError::POSIX); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) err.LogThreaded("::pthread_cancel ( thread = %p )", m_exception_thread); err.SetError(::pthread_join(m_exception_thread, NULL), DNBError::POSIX); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) err.LogThreaded("::pthread_join ( thread = %p, value_ptr = NULL)", m_exception_thread); // Deallocate our exception port that we used to track our child process mach_port_t task_self = mach_task_self (); err = ::mach_port_deallocate (task_self, exception_port); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) err.LogThreaded("::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )", task_self, exception_port); exception_port = NULL; return err.Error(); }
kern_return_t MachException::PortInfo::Save (task_t task) { DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Save ( task = 0x%4.4x )", task); // Be careful to be able to have debugserver built on a newer OS than what // it is currently running on by being able to start with all exceptions // and back off to just what is supported on the current system DNBError err; mask = EXC_MASK_ALL; count = (sizeof (ports) / sizeof (ports[0])); err = ::task_get_exception_ports (task, mask, masks, &count, ports, behaviors, flavors); if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, maskCnt => %u, ports, behaviors, flavors )", task, mask, count); if (err.Error() == KERN_INVALID_ARGUMENT && mask != PREV_EXC_MASK_ALL) { mask = PREV_EXC_MASK_ALL; count = (sizeof (ports) / sizeof (ports[0])); err = ::task_get_exception_ports (task, mask, masks, &count, ports, behaviors, flavors); if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, maskCnt => %u, ports, behaviors, flavors )", task, mask, count); } if (err.Fail()) { mask = 0; count = 0; } return err.Error(); }
//---------------------------------------------------------------------- // MachTask::BasicInfo //---------------------------------------------------------------------- kern_return_t MachTask::BasicInfo(task_t task, struct task_basic_info *info) { if (info == NULL) return KERN_INVALID_ARGUMENT; DNBError err; mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT; err = ::task_info (task, TASK_BASIC_INFO, (task_info_t)info, &count); const bool log_process = DNBLogCheckLogBit(LOG_TASK); if (log_process || err.Fail()) err.LogThreaded("::task_info ( target_task = 0x%4.4x, flavor = TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => %u )", task, info, count); if (DNBLogCheckLogBit(LOG_TASK) && DNBLogCheckLogBit(LOG_VERBOSE) && err.Success()) { float user = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f; float system = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f; DNBLogThreaded ("task_basic_info = { suspend_count = %i, virtual_size = 0x%8.8llx, resident_size = 0x%8.8llx, user_time = %f, system_time = %f }", info->suspend_count, (uint64_t)info->virtual_size, (uint64_t)info->resident_size, user, system); } return err.Error(); }
kern_return_t catch_mach_exception_raise_state_identity ( mach_port_t exc_port, mach_port_t thread_port, mach_port_t task_port, exception_type_t exc_type, mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count, int * flavor, thread_state_t old_state, mach_msg_type_number_t old_stateCnt, thread_state_t new_state, mach_msg_type_number_t *new_stateCnt ) { kern_return_t kret; if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })", __FUNCTION__, exc_port, thread_port, task_port, exc_type, MachException::Name(exc_type), exc_data_count, exc_data_count > 0 ? exc_data[0] : 0xBADDBADD, exc_data_count > 1 ? exc_data[1] : 0xBADDBADD); } kret = mach_port_deallocate (mach_task_self (), task_port); kret = mach_port_deallocate (mach_task_self (), thread_port); return KERN_FAILURE; }
rnb_err_t RNBSocket::Write (const void *buffer, size_t length) { if (m_fd == -1) return rnb_err; DNBError err; int bytessent = send (m_fd, buffer, length, 0); if (bytessent < 0) err.SetError(errno, DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::send ( socket = %i, buffer = %p, length = %zu, flags = 0 ) => %i", m_fd, buffer, length, bytessent); if (bytessent < 0) return rnb_err; if (bytessent != length) return rnb_err; DNBLogThreadedIf(LOG_RNB_PACKETS, "putpkt: %*s", (int)length, (char *)buffer); // All data is string based in debugserver, so this is safe DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, (char *)buffer); return rnb_success; }
kern_return_t catch_mach_exception_raise ( mach_port_t exc_port, mach_port_t thread_port, mach_port_t task_port, exception_type_t exc_type, mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count) { if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })", __FUNCTION__, exc_port, thread_port, task_port, exc_type, MachException::Name(exc_type), exc_data_count, exc_data_count > 0 ? exc_data[0] : 0xBADDBADD, exc_data_count > 1 ? exc_data[1] : 0xBADDBADD); } g_message->task_port = task_port; g_message->thread_port = thread_port; g_message->exc_type = exc_type; g_message->exc_data.resize(exc_data_count); ::memcpy (&g_message->exc_data[0], exc_data, g_message->exc_data.size() * sizeof (mach_exception_data_type_t)); return KERN_SUCCESS; }
bool MachException::Message::CatchExceptionRaise() { bool success = false; // locker will keep a mutex locked until it goes out of scope // PThreadMutex::Locker locker(&g_message_mutex); // DNBLogThreaded("calling mach_exc_server"); g_message = &state; // The exc_server function is the MIG generated server handling function // to handle messages from the kernel relating to the occurrence of an // exception in a thread. Such messages are delivered to the exception port // set via thread_set_exception_ports or task_set_exception_ports. When an // exception occurs in a thread, the thread sends an exception message to // its exception port, blocking in the kernel waiting for the receipt of a // reply. The exc_server function performs all necessary argument handling // for this kernel message and calls catch_exception_raise, // catch_exception_raise_state or catch_exception_raise_state_identity, // which should handle the exception. If the called routine returns // KERN_SUCCESS, a reply message will be sent, allowing the thread to // continue from the point of the exception; otherwise, no reply message // is sent and the called routine must have dealt with the exception // thread directly. if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr)) { success = true; } else if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { DNBLogThreaded("mach_exc_server returned zero..."); } g_message = NULL; return success; }
kern_return_t catch_mach_exception_raise_state ( mach_port_t exc_port, exception_type_t exc_type, const mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count, int * flavor, const thread_state_t old_state, mach_msg_type_number_t old_stateCnt, thread_state_t new_state, mach_msg_type_number_t * new_stateCnt ) { if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { DNBLogThreaded("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data = " MACH_EXCEPTION_DATA_FMT_HEX ", exc_data_count = %d)", __FUNCTION__, exc_port, exc_type, MachException::Name(exc_type), exc_data, exc_data_count); } return KERN_FAILURE; }
//---------------------------------------------------------------------- // MachTask::Resume //---------------------------------------------------------------------- kern_return_t MachTask::Resume() { struct task_basic_info task_info; task_t task = TaskPort(); if (task == TASK_NULL) return KERN_INVALID_ARGUMENT; DNBError err; err = BasicInfo(task, &task_info); if (err.Success()) { // task_resume isn't counted like task_suspend calls are, are, so if the // task is not suspended, don't try and resume it since it is already // running if (task_info.suspend_count > 0) { err = ::task_resume (task); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) err.LogThreaded("::task_resume ( target_task = 0x%4.4x )", task); } } return err.Error(); }
//---------------------------------------------------------------------- // MachTask::WriteMemory //---------------------------------------------------------------------- nub_size_t MachTask::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf) { nub_size_t n = 0; task_t task = TaskPort(); if (task != TASK_NULL) { n = m_vm_memory.Write(task, addr, buf, size); DNBLogThreadedIf(LOG_MEMORY, "MachTask::WriteMemory ( addr = 0x%8.8llx, size = %zu, buf = %8.8p) => %u bytes written", (uint64_t)addr, size, buf, n); if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) { DNBDataRef data((uint8_t*)buf, n, false); data.Dump(0, n, addr, DNBDataRef::TypeUInt8, 16); } } return n; }
//---------------------------------------------------------------------- // MachTask::Suspend //---------------------------------------------------------------------- kern_return_t MachTask::Suspend() { DNBError err; task_t task = TaskPort(); err = ::task_suspend (task); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) err.LogThreaded("::task_suspend ( target_task = 0x%4.4x )", task); return err.Error(); }
bool MachThread::RestoreSuspendCountAfterStop () { DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); DNBError err; if (ThreadIDIsValid(m_tid) == false) return false; if (m_suspend_count > 0) { while (m_suspend_count > 0) { err = ::thread_resume (m_tid); if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) err.LogThreaded("::thread_resume (%4.4x)", m_tid); if (err.Success()) --m_suspend_count; else { if (GetBasicInfo()) m_suspend_count = m_basic_info.suspend_count; else m_suspend_count = 0; return false; // ??? } } } else if (m_suspend_count < 0) { while (m_suspend_count < 0) { err = ::thread_suspend (m_tid); if (err.Success()) ++m_suspend_count; if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) { err.LogThreaded("::thread_suspend (%4.4x)", m_tid); return false; } } } return true; }
bool MachTask::StartExceptionThread(DNBError &err) { DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( )", __FUNCTION__); task_t task = TaskPortForProcessID(err); if (MachTask::IsValid(task)) { // Got the mach port for the current process mach_port_t task_self = mach_task_self (); // Allocate an exception port that we will use to track our child process err = ::mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &m_exception_port); if (err.Fail()) return false; // Add the ability to send messages on the new exception port err = ::mach_port_insert_right (task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND); if (err.Fail()) return false; // Save the original state of the exception ports for our child process SaveExceptionPortInfo(); // We weren't able to save the info for our exception ports, we must stop... if (m_exc_port_info.mask == 0) { err.SetErrorString("failed to get exception port info"); return false; } // Set the ability to get all exceptions on this port err = ::task_set_exception_ports (task, m_exc_port_info.mask, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) { err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior = 0x%8.8x, new_flavor = 0x%8.8x )", task, m_exc_port_info.mask, m_exception_port, (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES), THREAD_STATE_NONE); } if (err.Fail()) return false; // Create the exception thread err = ::pthread_create (&m_exception_thread, NULL, MachTask::ExceptionThread, this); return err.Success(); } else { DNBLogError("MachTask::%s (): task invalid, exception thread start failed.", __FUNCTION__); } return false; }
void MachThread::Suspend() { DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); if (MachPortNumberIsValid(m_mach_port_number)) { DNBError err(::thread_suspend(m_mach_port_number), DNBError::MachKernel); if (err.Success()) m_suspend_count++; if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")", m_mach_port_number); } }
nub_size_t MachVMMemory::Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count) { if (data == NULL || data_count == 0) return 0; nub_size_t total_bytes_read = 0; nub_addr_t curr_addr = address; uint8_t *curr_data = (uint8_t*)data; while (total_bytes_read < data_count) { mach_vm_size_t curr_size = MaxBytesLeftInPage(task, curr_addr, data_count - total_bytes_read); mach_msg_type_number_t curr_bytes_read = 0; vm_offset_t vm_memory = NULL; m_err = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read); if (DNBLogCheckLogBit(LOG_MEMORY)) m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt => %i )", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read); if (m_err.Success()) { if (curr_bytes_read != curr_size) { if (DNBLogCheckLogBit(LOG_MEMORY)) m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt=>%i ) only read %u of %llu bytes", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read, curr_bytes_read, (uint64_t)curr_size); } ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read); ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read); total_bytes_read += curr_bytes_read; curr_addr += curr_bytes_read; curr_data += curr_bytes_read; } else { break; } } return total_bytes_read; }
kern_return_t MachException::PortInfo::Save (task_t task) { count = (sizeof (ports) / sizeof (ports[0])); DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Save ( task = 0x%4.4x )", task); DNBError err; err = ::task_get_exception_ports (task, EXC_MASK_ALL, masks, &count, ports, behaviors, flavors); if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, maskCnt => %u, ports, behaviors, flavors )", task, EXC_MASK_ALL, count); if (err.Fail()) count = 0; return err.Error(); }
void MachThread::Suspend() { DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); if (ThreadIDIsValid(m_tid)) { DNBError err(::thread_suspend (m_tid), DNBError::MachKernel); if (err.Success()) m_suspend_count++; if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) err.LogThreaded("::thread_suspend (%4.4x)", m_tid); } }
kern_return_t MachException::Message::Receive(mach_port_t port, mach_msg_option_t options, mach_msg_timeout_t timeout, mach_port_t notify_port) { DNBError err; const bool log_exceptions = DNBLogCheckLogBit(LOG_EXCEPTIONS); mach_msg_timeout_t mach_msg_timeout = options & MACH_RCV_TIMEOUT ? timeout : 0; if (log_exceptions && ((options & MACH_RCV_TIMEOUT) == 0)) { // Dump this log message if we have no timeout in case it never returns DNBLogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)", exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 0, sizeof (exc_msg.data), port, mach_msg_timeout, notify_port); } err = ::mach_msg (&exc_msg.hdr, options, // options 0, // Send size sizeof (exc_msg.data), // Receive size port, // exception port to watch for exception on mach_msg_timeout, // timeout in msec (obeyed only if MACH_RCV_TIMEOUT is ORed into the options parameter) notify_port); // Dump any errors we get if (log_exceptions) { err.LogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)", exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 0, sizeof (exc_msg.data), port, mach_msg_timeout, notify_port); } return err.Error(); }
nub_size_t MachVMMemory::WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count) { if (data == NULL || data_count == 0) return 0; nub_size_t total_bytes_written = 0; nub_addr_t curr_addr = address; const uint8_t *curr_data = (const uint8_t*)data; while (total_bytes_written < data_count) { mach_msg_type_number_t curr_data_count = MaxBytesLeftInPage(task, curr_addr, data_count - total_bytes_written); m_err = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count); if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail()) m_err.LogThreaded("::mach_vm_write ( task = 0x%4.4x, addr = 0x%8.8llx, data = %8.8p, dataCnt = %u )", task, (uint64_t)curr_addr, curr_data, curr_data_count); #if !defined (__i386__) && !defined (__x86_64__) vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH; m_err = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value); if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail()) m_err.LogThreaded("::vm_machine_attribute ( task = 0x%4.4x, addr = 0x%8.8llx, size = %u, attr = MATTR_CACHE, mattr_value => MATTR_VAL_CACHE_FLUSH )", task, (uint64_t)curr_addr, curr_data_count); #endif if (m_err.Success()) { total_bytes_written += curr_data_count; curr_addr += curr_data_count; curr_data += curr_data_count; } else { break; } } return total_bytes_written; }
uint32_t MachThread::Resume() { DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); if (ThreadIDIsValid(m_tid)) { while (m_suspendCount > 0) { DNBError err(::thread_resume (m_tid), DNBError::MachKernel); if (err.Success()) m_suspendCount--; if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) err.LogThreaded("::thread_resume (%4.4x)", m_tid); } } return SuspendCount(); }
bool MachThread::SetSuspendCountBeforeResume(bool others_stopped) { DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); DNBError err; if (ThreadIDIsValid(m_tid) == false) return false; size_t times_to_resume; if (others_stopped) { if (GetBasicInfo()) { times_to_resume = m_basic_info.suspend_count; m_suspend_count = - (times_to_resume - m_suspend_count); } else times_to_resume = 0; } else { times_to_resume = m_suspend_count; m_suspend_count = 0; } if (times_to_resume > 0) { while (times_to_resume > 0) { err = ::thread_resume (m_tid); if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) err.LogThreaded("::thread_resume (%4.4x)", m_tid); if (err.Success()) --times_to_resume; else { if (GetBasicInfo()) times_to_resume = m_basic_info.suspend_count; else times_to_resume = 0; } } } return true; }
bool MachThread::RestoreSuspendCount() { DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); DNBError err; if (ThreadIDIsValid(m_tid) == false) return false; if (m_suspendCount > 0) { while (m_suspendCount > 0) { err = ::thread_resume (m_tid); if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) err.LogThreaded("::thread_resume (%4.4x)", m_tid); if (err.Success()) --m_suspendCount; else { if (GetBasicInfo()) m_suspendCount = m_basicInfo.suspend_count; else m_suspendCount = 0; return false; // ??? } } } // We don't currently really support resuming a thread that was externally // suspended. If/when we do, we will need to make the code below work and // m_suspendCount will need to become signed instead of unsigned. // else if (m_suspendCount < 0) // { // while (m_suspendCount < 0) // { // err = ::thread_suspend (m_tid); // if (err.Success()) // ++m_suspendCount; // if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) // err.LogThreaded("::thread_suspend (%4.4x)", m_tid); // } // } return true; }
rnb_err_t RNBSocket::Read (std::string &p) { char buf[1024]; p.clear(); // Note that BUF is on the stack so we must be careful to keep any // writes to BUF from overflowing or we'll have security issues. if (m_fd == -1) return rnb_err; //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); DNBError err; int bytesread = read (m_fd, buf, sizeof (buf)); if (bytesread <= 0) err.SetError(errno, DNBError::POSIX); else p.append(buf, bytesread); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::read ( %i, %p, %zu ) => %i", m_fd, buf, sizeof (buf), bytesread); // Our port went away - we have to mark this so IsConnected will return the truth. if (bytesread == 0) { m_fd = -1; return rnb_not_connected; } else if (bytesread == -1) { m_fd = -1; return rnb_err; } // Strip spaces from the end of the buffer while (!p.empty() && isspace (p[p.size() - 1])) p.erase (p.size () - 1); // Most data in the debugserver packets valid printable characters... DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str()); return rnb_success; }
kern_return_t MachException::PortInfo::Restore (task_t task) { DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Restore( task = 0x%4.4x )", task); uint32_t i = 0; DNBError err; if (count > 0) { for (i = 0; i < count; i++) { err = ::task_set_exception_ports (task, masks[i], ports[i], behaviors[i], flavors[i]); if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) { err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior = 0x%8.8x, new_flavor = 0x%8.8x )", task, masks[i], ports[i], behaviors[i], flavors[i]); // Bail if we encounter any errors } if (err.Fail()) break; } } count = 0; return err.Error(); }
//---------------------------------------------------------------------- // MachTask::TaskPortForProcessID //---------------------------------------------------------------------- task_t MachTask::TaskPortForProcessID (pid_t pid, DNBError &err, uint32_t num_retries, uint32_t usec_interval) { if (pid != INVALID_NUB_PROCESS) { DNBError err; mach_port_t task_self = mach_task_self (); task_t task = TASK_NULL; for (uint32_t i=0; i<num_retries; i++) { err = ::task_for_pid ( task_self, pid, &task); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) { char str[1024]; ::snprintf (str, sizeof(str), "::task_for_pid ( target_tport = 0x%4.4x, pid = %d, &task ) => err = 0x%8.8x (%s)", task_self, pid, err.Error(), err.AsString() ? err.AsString() : "success"); if (err.Fail()) err.SetErrorString(str); err.LogThreaded(str); } if (err.Success()) return task; // Sleep a bit and try again ::usleep (usec_interval); } } return TASK_NULL; }
rnb_err_t RNBSocket::Listen (in_port_t port, PortBoundCallback callback, const void *callback_baton) { //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); // Disconnect without saving errno Disconnect (false); DNBError err; int listen_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listen_fd == -1) err.SetError(errno, DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol = IPPROTO_TCP ) => socket = %i", listen_fd); if (err.Fail()) return rnb_err; // enable local address reuse SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1); struct sockaddr_in sa; ::memset (&sa, 0, sizeof sa); sa.sin_len = sizeof sa; sa.sin_family = AF_INET; sa.sin_port = htons (port); sa.sin_addr.s_addr = htonl (INADDR_ANY); int error = ::bind (listen_fd, (struct sockaddr *) &sa, sizeof(sa)); if (error == -1) err.SetError(errno, DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )", listen_fd); if (err.Fail()) { ClosePort (listen_fd, false); return rnb_err; } if (callback && port == 0) { // We were asked to listen on port zero which means we // must now read the actual port that was given to us // as port zero is a special code for "find an open port // for me". socklen_t sa_len = sizeof (sa); if (getsockname(listen_fd, (struct sockaddr *)&sa, &sa_len) == 0) { port = ntohs (sa.sin_port); callback (callback_baton, port); } } error = ::listen (listen_fd, 1); if (error == -1) err.SetError(errno, DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_fd); if (err.Fail()) { ClosePort (listen_fd, false); return rnb_err; } m_fd = ::accept (listen_fd, NULL, 0); if (m_fd == -1) err.SetError(errno, DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::accept ( socket = %i, address = NULL, address_len = 0 )", listen_fd); ClosePort (listen_fd, false); if (err.Fail()) { return rnb_err; } else { // Keep our TCP packets coming without any delays. SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1); } return rnb_success; }
rnb_err_t RNBSocket::Listen (const char *listen_host, uint16_t port, PortBoundCallback callback, const void *callback_baton) { //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); // Disconnect without saving errno Disconnect (false); // Now figure out the hostname that will be attaching and palce it into struct sockaddr_in listen_addr; ::memset (&listen_addr, 0, sizeof listen_addr); listen_addr.sin_len = sizeof listen_addr; listen_addr.sin_family = AF_INET; listen_addr.sin_port = htons (port); listen_addr.sin_addr.s_addr = INADDR_ANY; if (!ResolveIPV4HostName(listen_host, listen_addr.sin_addr.s_addr)) { DNBLogThreaded("error: failed to resolve connecting host '%s'", listen_host); return rnb_err; } DNBError err; int listen_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listen_fd == -1) err.SetError(errno, DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol = IPPROTO_TCP ) => socket = %i", listen_fd); if (err.Fail()) return rnb_err; // enable local address reuse SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1); struct sockaddr_in sa; ::memset (&sa, 0, sizeof sa); sa.sin_len = sizeof sa; sa.sin_family = AF_INET; sa.sin_port = htons (port); sa.sin_addr.s_addr = INADDR_ANY; // Let incoming connections bind to any host network interface (this is NOT who can connect to us) int error = ::bind (listen_fd, (struct sockaddr *) &sa, sizeof(sa)); if (error == -1) err.SetError(errno, DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )", listen_fd); if (err.Fail()) { ClosePort (listen_fd, false); return rnb_err; } error = ::listen (listen_fd, 5); if (error == -1) err.SetError(errno, DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_fd); if (err.Fail()) { ClosePort (listen_fd, false); return rnb_err; } if (callback) { // We were asked to listen on port zero which means we // must now read the actual port that was given to us // as port zero is a special code for "find an open port // for me". if (port == 0) { socklen_t sa_len = sizeof (sa); if (getsockname(listen_fd, (struct sockaddr *)&sa, &sa_len) == 0) { port = ntohs (sa.sin_port); callback (callback_baton, port); } } else { callback (callback_baton, port); } } struct sockaddr_in accept_addr; ::memset (&accept_addr, 0, sizeof accept_addr); accept_addr.sin_len = sizeof accept_addr; bool accept_connection = false; // Loop until we are happy with our connection while (!accept_connection) { socklen_t accept_addr_len = sizeof accept_addr; m_fd = ::accept (listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len); if (m_fd == -1) err.SetError(errno, DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) err.LogThreaded("::accept ( socket = %i, address = %p, address_len = %u )", listen_fd, &accept_addr, accept_addr_len); if (err.Fail()) break; if (listen_addr.sin_addr.s_addr == INADDR_ANY) accept_connection = true; else { if (accept_addr_len == listen_addr.sin_len && accept_addr.sin_addr.s_addr == listen_addr.sin_addr.s_addr) { accept_connection = true; } else { ::close (m_fd); m_fd = -1; const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr; const uint8_t *listen_ip = (const uint8_t *)&listen_addr.sin_addr.s_addr; ::fprintf (stderr, "error: rejecting incoming connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)\n", accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3], listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]); DNBLogThreaded ("error: rejecting connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)", accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3], listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]); } } } ClosePort (listen_fd, false); if (err.Fail()) { return rnb_err; } else { // Keep our TCP packets coming without any delays. SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1); } return rnb_success; }
kern_return_t MachException::Message::Reply(MachProcess *process, int signal) { // Reply to the exception... DNBError err; // If we had a soft signal, we need to update the thread first so it can // continue without signaling int soft_signal = state.SoftSignal(); if (soft_signal) { int state_pid = -1; if (process->Task().TaskPort() == state.task_port) { // This is our task, so we can update the signal to send to it state_pid = process->ProcessID(); soft_signal = signal; } else { err = ::pid_for_task(state.task_port, &state_pid); } assert (state_pid != -1); if (state_pid != -1) { errno = 0; if (::ptrace (PT_THUPDATE, state_pid, (caddr_t)state.thread_port, soft_signal) != 0) err.SetError(errno, DNBError::POSIX); else err.Clear(); if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) err.LogThreaded("::ptrace (request = PT_THUPDATE, pid = 0x%4.4x, tid = 0x%4.4x, signal = %i)", state_pid, state.thread_port, soft_signal); } } DNBLogThreadedIf(LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)", reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size, reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port, reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id, MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply_msg.hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); err = ::mach_msg ( &reply_msg.hdr, MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply_msg.hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (err.Fail()) { if (err.Error() == MACH_SEND_INTERRUPTED) { if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) err.LogThreaded("::mach_msg() - send interrupted"); // TODO: keep retrying to reply??? } else { if (state.task_port == process->Task().TaskPort()) { if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) err.LogThreaded("::mach_msg() - failed (task)"); abort (); } else { if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) err.LogThreaded("::mach_msg() - failed (child of task)"); } } } return err.Error(); }
uint32_t MachThreadList::UpdateThreadList(MachProcess *process, bool update, MachThreadList::collection *new_threads) { // locker will keep a mutex locked until it goes out of scope DNBLogThreadedIf (LOG_THREAD, "MachThreadList::UpdateThreadList (pid = %4.4x, update = %u) process stop count = %u", process->ProcessID(), update, process->StopCount()); PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); #if defined (__i386__) || defined (__x86_64__) if (process->StopCount() == 0) { int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process->ProcessID() }; struct kinfo_proc processInfo; size_t bufsize = sizeof(processInfo); bool is_64_bit = false; if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0) { if (processInfo.kp_proc.p_flag & P_LP64) is_64_bit = true; } if (is_64_bit) DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64); else DNBArchProtocol::SetArchitecture(CPU_TYPE_I386); } #endif if (m_threads.empty() || update) { thread_array_t thread_list = NULL; mach_msg_type_number_t thread_list_count = 0; task_t task = process->Task().TaskPort(); DNBError err(::task_threads (task, &thread_list, &thread_list_count), DNBError::MachKernel); if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) err.LogThreaded("::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count); if (err.Error() == KERN_SUCCESS && thread_list_count > 0) { MachThreadList::collection currThreads; size_t idx; // Iterator through the current thread list and see which threads // we already have in our list (keep them), which ones we don't // (add them), and which ones are not around anymore (remove them). for (idx = 0; idx < thread_list_count; ++idx) { const thread_t tid = thread_list[idx]; MachThreadSP thread_sp (GetThreadByID (tid)); if (thread_sp) { // Keep the existing thread class currThreads.push_back(thread_sp); } else { // We don't have this thread, lets add it. thread_sp.reset(new MachThread(process, tid)); // Add the new thread regardless of its is user ready state... // Make sure the thread is ready to be displayed and shown to users // before we add this thread to our list... if (thread_sp->IsUserReady()) { if (new_threads) new_threads->push_back(thread_sp); currThreads.push_back(thread_sp); } } } m_threads.swap(currThreads); m_current_thread.reset(); // Free the vm memory given to us by ::task_threads() vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (thread_t)); ::vm_deallocate (::mach_task_self(), (vm_address_t)thread_list, thread_list_size); } } return m_threads.size(); }