diff options
Diffstat (limited to 'lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp')
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp b/lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp new file mode 100644 index 00000000000..76288c55bf5 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp @@ -0,0 +1,679 @@ +//===-- MachDYLD.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/29/07. +// +//===----------------------------------------------------------------------===// + +#include "MachDYLD.h" +#include "DNB.h" +#include "DNBDataRef.h" +#include <mach-o/loader.h> +#include "DNBLog.h" + +MachDYLD::MachDYLD() : + m_pid(INVALID_NUB_PROCESS), + m_addr_size(4), + m_dyld_addr(INVALID_NUB_ADDRESS), + m_dyld_all_image_infos_addr(INVALID_NUB_ADDRESS), + m_dylib_info_header(), + m_current_dylibs(), + m_changed_dylibs(), + m_notify_break_id(INVALID_NUB_BREAK_ID), + m_dyld_info_mutex(PTHREAD_MUTEX_RECURSIVE) +{ +} + +MachDYLD::~MachDYLD() +{ + Clear(); +} + + +void +MachDYLD::Clear() +{ + PThreadMutex::Locker locker(m_dyld_info_mutex); + + nub_process_t pid = m_pid; + if (pid != INVALID_NUB_PROCESS) + { + DNBProcessSetSharedLibraryInfoCallback ( pid, NULL, NULL); + DNBBreakpointClear(pid, m_notify_break_id); + } + + m_addr_size = 4; + m_dyld_addr = INVALID_NUB_ADDRESS; + m_dyld_all_image_infos_addr = INVALID_NUB_ADDRESS; + m_dylib_info_header.Clear(); + m_current_dylibs.clear(); + m_changed_dylibs.clear(); + m_notify_break_id = INVALID_NUB_BREAK_ID; +} + + +void +MachDYLD::Initialize(nub_process_t pid) +{ + //printf("MachDYLD::%s(0x%4.4x)\n", __FUNCTION__, pid); + Clear(); + m_pid = pid; +} + + +void +MachDYLD::ProcessStateChanged(nub_state_t state) +{ + //printf("MachDYLD::%s(%s)\n", __FUNCTION__, DNBStateAsString(state)); + + switch (state) + { + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + case eStateDetached: + case eStateAttaching: + case eStateLaunching: + Clear(); + break; + + case eStateStopped: + // Keep trying find dyld each time we stop until we do + if (!FoundDYLD()) + { + assert(m_pid != INVALID_NUB_PROCESS); + DNBProcessSetSharedLibraryInfoCallback ( m_pid, CopySharedInfoCallback, this); + CheckForDYLDInMemory(); + } + break; + + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + break; + + default: + break; + } +} + +void +MachDYLD::SharedLibraryStateChanged(DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos) +{ + //printf("MachDYLD::%s(%p, %u)\n", __FUNCTION__, image_infos, image_infos); + +} + +bool +MachDYLD::FoundDYLD() const +{ + return m_dyld_addr != INVALID_NUB_ADDRESS; +} + +bool +MachDYLD::CheckForDYLDInMemory() +{ +#if defined (__arm__) + return CheckForDYLDInMemory(0x2fe00000); +#else + return CheckForDYLDInMemory(0x8fe00000); +#endif +} + +bool +MachDYLD::CheckForDYLDInMemory(nub_addr_t addr) +{ + std::vector<uint8_t> dyld_header; + nub_size_t page_size = 0x1000; + dyld_header.resize(page_size); + nub_size_t bytes_read = DNBProcessMemoryRead(m_pid, addr, dyld_header.size(), &dyld_header[0]); + if (bytes_read > 0) + { + DNBDataRef::offset_t offset = 0; + DNBDataRef data(&dyld_header[0], bytes_read, false); + struct mach_header *header = (struct mach_header*)data.GetData(&offset, sizeof(struct mach_header)); + if (header) + { + switch (header->magic) + { + case MH_MAGIC: + case MH_CIGAM: + data.SetPointerSize(4); + m_addr_size = 4; + break; + + case MH_MAGIC_64: + case MH_CIGAM_64: + data.SetPointerSize(8); + m_addr_size = 8; + break; + + default: + return false; + } + + if (header->filetype == MH_DYLINKER) + { + // printf( "Found DYLD mach image at %8.8p", addr); + + m_dyld_all_image_infos_addr = DNBProcessLookupAddress(m_pid, "dyld_all_image_infos", "/usr/lib/dyld"); + +#if defined (__arm__) + m_dyld_all_image_infos_addr = 0x2fe3a004; +#endif + + if (m_dyld_all_image_infos_addr != INVALID_NUB_ADDRESS) + { + // printf( "Found DYLD data symbol 'dyld_all_image_infos' is %8.8p", m_dyld_all_image_infos_addr); + + if (ReadDYLIBInfo()) + { + if (m_dylib_info_header.notification != INVALID_NUB_ADDRESS) + { + m_notify_break_id = DNBBreakpointSet(m_pid, m_dylib_info_header.notification, 4, true); + if (NUB_BREAK_ID_IS_VALID(m_notify_break_id)) + { + DNBBreakpointSetCallback(m_pid, m_notify_break_id, MachDYLD::BreakpointHit, this); + m_dyld_addr = addr; + } + } + } + // if (DNBLogCheckLogBit(LOG_SHLIB)) + // Dump(DNBLogGetLogFile()); + } + return true; + } + } + } + return false; +} + +nub_bool_t +MachDYLD::BreakpointHit(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton) +{ + MachDYLD *dyld = (MachDYLD*) baton; + //printf("MachDYLD::BreakpointHit called"); + dyld->ReadDYLIBInfo(); + DNBProcessSharedLibrariesUpdated(pid); + return false; // Don't stop the process, let it continue +} + +bool +MachDYLD::ReadDYLIBInfo() +{ + nub_addr_t addr = m_dyld_all_image_infos_addr; + if (addr != INVALID_NUB_ADDRESS) + { + PThreadMutex::Locker locker(m_dyld_info_mutex); + //printf("MachDYLD::ReadDYLIBInfo(addr =%8.8p)", addr); + bool swap = false; + uint32_t i = 0; + DYLIBInfo::collection previous_dylibs; + previous_dylibs.swap(m_current_dylibs); + uint8_t all_dylib_info_data[32]; + nub_size_t count = 8 + m_addr_size * 2; + nub_size_t bytes_read = DNBProcessMemoryRead(m_pid, addr, count, &all_dylib_info_data[0]); + if (bytes_read != count) + { + m_dylib_info_header.Clear(); + return false; + } + + DNBDataRef data(all_dylib_info_data, sizeof(all_dylib_info_data), swap); + data.SetPointerSize(m_addr_size); + DNBDataRef::offset_t offset = 0; + m_dylib_info_header.version = data.Get32(&offset); + m_dylib_info_header.dylib_info_count = data.Get32(&offset); + m_dylib_info_header.dylib_info_addr = data.GetPointer(&offset); + m_dylib_info_header.notification = data.GetPointer(&offset); + //printf( "%s: version=%d, count=%d, addr=%8.8p, notify=%8.8p", + // __PRETTY_FUNCTION__, + // m_dylib_info_header.version, + // m_dylib_info_header.dylib_info_count, + // m_dylib_info_header.dylib_info_addr, + // m_dylib_info_header.notification); + + switch (m_dylib_info_header.version) + { + case 1: // 10.4.x and prior + { + } + break; + + case 2: // 10.5 and later + { + } + break; + + default: + //printf( "Invalid dyld all_dylib_infos version number: %d", m_dylib_info_header.version); + return false; + break; + } + + // If we made it here, we are assuming that the all dylib info data should + // be valid, lets read the info array. + if (m_dylib_info_header.dylib_info_count > 0) + { + if (m_dylib_info_header.dylib_info_addr == 0) + { + //printf( "dyld is currently updating all_dylib_infos."); + } + else + { + m_current_dylibs.resize(m_dylib_info_header.dylib_info_count); + count = m_current_dylibs.size() * 3 * m_addr_size; + std::vector<uint8_t> info_data(count, 0); + bytes_read = DNBProcessMemoryRead(m_pid, m_dylib_info_header.dylib_info_addr, count, &info_data[0]); + if (bytes_read == count) + { + DNBDataRef::offset_t info_data_offset = 0; + DNBDataRef info_data_ref(&info_data[0], info_data.size(), swap); + info_data_ref.SetPointerSize(m_addr_size); + for (i = 0; info_data_ref.ValidOffset(info_data_offset); i++) + { + assert (i < m_current_dylibs.size()); + m_current_dylibs[i].address = info_data_ref.GetPointer(&info_data_offset); + nub_addr_t path_addr = info_data_ref.GetPointer(&info_data_offset); + m_current_dylibs[i].mod_date = info_data_ref.GetPointer(&info_data_offset); + + char raw_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + bytes_read = DNBProcessMemoryRead(m_pid, path_addr, sizeof(raw_path), (char*)&raw_path[0]); + if (::realpath(raw_path, resolved_path)) + m_current_dylibs[i].path = resolved_path; + else + m_current_dylibs[i].path = raw_path; + } + assert(i == m_dylib_info_header.dylib_info_count); + + UpdateUUIDs(); + } + else + { + //printf( "unable to read all data for all_dylib_infos."); + m_current_dylibs.clear(); + return false; + } + } + } + // Read any UUID values that we can get + if (m_current_dylibs.empty()) + { + m_changed_dylibs = previous_dylibs; + const size_t num_changed_dylibs = m_changed_dylibs.size(); + for (i = 0; i < num_changed_dylibs; i++) + { + // Indicate the shared library was unloaded by giving it an invalid + // address... + m_changed_dylibs[i].address = INVALID_NUB_ADDRESS; + } + } + else + { + m_changed_dylibs.clear(); + + // Most of the time when we get shared library changes, they just + // get appended to the end of the list, so find out the min number + // of entries in the current and previous list that match and see + // how many are equal. + uint32_t curr_dylib_count = m_current_dylibs.size(); + uint32_t prev_dylib_count = previous_dylibs.size(); + uint32_t common_count = std::min<uint32_t>(prev_dylib_count, curr_dylib_count); + MachDYLD::DYLIBInfo::const_iterator curr_pos = m_current_dylibs.begin(); + MachDYLD::DYLIBInfo::const_iterator curr_end = m_current_dylibs.end(); + MachDYLD::DYLIBInfo::iterator prev_pos = previous_dylibs.begin(); + uint32_t idx; + for (idx = 0; idx < common_count; idx++) + { + if (*curr_pos == *prev_pos) + { + ++curr_pos; + ++prev_pos; + } + else + break; + } + + // Remove all the entries that were at the exact same index and that + // matched between the previous_dylibs and m_current_dylibs arrays. This will cover + // most of the cases as when shared libraries get loaded they get + // appended to the end of the list. + if (prev_pos != previous_dylibs.begin()) + { + previous_dylibs.erase(previous_dylibs.begin(), prev_pos); + } + + if (previous_dylibs.empty()) + { + // We only have new libraries to add, they are the only ones that + // have changed. + if (curr_pos != curr_end) + { + m_changed_dylibs.assign(curr_pos, curr_end); + } + } + else + { + // We still have items in our previous dylib list which means either + // one or more shared libraries got unloaded somewhere in the middle + // of the list, so we will manually search for each remaining item + // in our current list in the previous list + for (; curr_pos != curr_end; ++curr_pos) + { + MachDYLD::DYLIBInfo::iterator pos = std::find(previous_dylibs.begin(), previous_dylibs.end(), *curr_pos); + if (pos == previous_dylibs.end()) + { + // This dylib wasn't here before, add it to our change list + m_changed_dylibs.push_back(*curr_pos); + } + else + { + // This dylib was in our previous dylib list, it didn't + // change, so lets remove it from the previous list so we + // don't see it again. + previous_dylibs.erase(pos); + } + } + + // The only items left if our previous_dylibs array will be shared + // libraries that got unloaded (still in previous list, and not + // mentioned in the current list). + if (!previous_dylibs.empty()) + { + const size_t num_previous_dylibs = previous_dylibs.size(); + for (i = 0; i < num_previous_dylibs; i++) + { + // Indicate the shared library was unloaded by giving it + // an invalid address... + previous_dylibs[i].address = INVALID_NUB_ADDRESS; + } + // Add all remaining previous_dylibs to the changed list with + // invalidated addresses so we know they got unloaded. + m_changed_dylibs.insert(m_changed_dylibs.end(), previous_dylibs.begin(), previous_dylibs.end()); + } + } + } + return true; + } + return false; +} + + +void +MachDYLD::UpdateUUIDs() +{ + bool swap = false; + nub_size_t page_size = 0x1000; + uint32_t i; + // Read any UUID values that we can get + for (i = 0; i < m_dylib_info_header.dylib_info_count; i++) + { + if (!m_current_dylibs[i].UUIDValid()) + { + std::vector<uint8_t> bytes(page_size, 0); + nub_size_t bytes_read = DNBProcessMemoryRead(m_pid, m_current_dylibs[i].address, page_size, &bytes[0]); + if (bytes_read > 0) + { + DNBDataRef::offset_t offset = 0; + DNBDataRef data(&bytes[0], bytes_read, swap); + struct mach_header *header = (struct mach_header*)data.GetData(&offset, sizeof(struct mach_header)); + if (header) + { + switch (header->magic) + { + case MH_MAGIC: + case MH_CIGAM: + data.SetPointerSize(4); + m_addr_size = 4; + break; + + case MH_MAGIC_64: + case MH_CIGAM_64: + data.SetPointerSize(8); + m_addr_size = 8; + offset += 4; // Skip the extra reserved field in the 64 bit mach header + break; + + default: + continue; + } + + if (header->sizeofcmds > bytes_read) + { + bytes.resize(header->sizeofcmds); + nub_addr_t addr = m_current_dylibs[i].address + bytes_read; + bytes_read += DNBProcessMemoryRead(m_pid, addr , header->sizeofcmds - bytes_read, &bytes[bytes_read]); + } + assert(bytes_read >= header->sizeofcmds); + uint32_t cmd_idx; + DNBSegment segment; + + for (cmd_idx = 0; cmd_idx < header->ncmds; cmd_idx++) + { + if (data.ValidOffsetForDataOfSize(offset, sizeof(struct load_command))) + { + struct load_command load_cmd; + DNBDataRef::offset_t load_cmd_offset = offset; + load_cmd.cmd = data.Get32(&offset); + load_cmd.cmdsize = data.Get32(&offset); + switch (load_cmd.cmd) + { + case LC_SEGMENT: + { + strncpy(segment.name, data.GetCStr(&offset, 16), 16); + memset(&segment.name[16], 0, DNB_MAX_SEGMENT_NAME_LENGTH - 16); + segment.addr = data.Get32(&offset); + segment.size = data.Get32(&offset); + m_current_dylibs[i].segments.push_back(segment); + } + break; + + case LC_SEGMENT_64: + { + strncpy(segment.name, data.GetCStr(&offset, 16), 16); + memset(&segment.name[16], 0, DNB_MAX_SEGMENT_NAME_LENGTH - 16); + segment.addr = data.Get64(&offset); + segment.size = data.Get64(&offset); + m_current_dylibs[i].segments.push_back(segment); + } + break; + + case LC_UUID: + // We found our UUID, we can stop now... + memcpy(m_current_dylibs[i].uuid, data.GetData(&offset, 16), 16); + // if (DNBLogCheckLogBit(LOG_SHLIB)) + // { + // DNBLogThreaded("UUID found for aii[%d]:", i); + // m_current_dylibs[i].Dump(DNBLogGetLogFile()); + // } + break; + + default: + break; + } + // Set offset to be the beginning of the next load command. + offset = load_cmd_offset + load_cmd.cmdsize; + } + } + } + } + } + } +} + + +nub_addr_t +MachDYLD::GetSharedLibraryHeaderAddress(const char *shlib_path) const +{ + if (!m_current_dylibs.empty() && shlib_path && shlib_path[0]) + { + uint32_t i; + for (i = 0; i<m_current_dylibs.size(); i++) + { + if (m_current_dylibs[i].path == shlib_path) + return m_current_dylibs[i].address; + } + } + return INVALID_NUB_ADDRESS; +} + + +nub_size_t +MachDYLD::CopySharedLibraryInfo(DYLIBInfo::collection& dylib_coll, DNBExecutableImageInfo **image_infos) +{ + if (!dylib_coll.empty()) + { + size_t i; + size_t total_num_segments = 0; + size_t segment_index = 0; + for (i = 0; i<dylib_coll.size(); i++) + { + total_num_segments += dylib_coll[i].segments.size(); + } + size_t image_infos_byte_size = sizeof(DNBExecutableImageInfo) * dylib_coll.size(); + size_t all_segments_byte_size = sizeof(DNBSegment) * total_num_segments; + size_t total_byte_size = image_infos_byte_size + all_segments_byte_size; + + // Allocate enough space to fit all of the shared library information in + // a single buffer so consumers can free a single chunk of data when done + uint8_t *buf = (uint8_t*)malloc (total_byte_size); + + DNBExecutableImageInfo *info = (DNBExecutableImageInfo*)buf; + DNBSegment *all_segments = (DNBSegment*)(buf + image_infos_byte_size); + if (info) + { + for (i = 0; i<dylib_coll.size(); i++) + { + strncpy(info[i].name, dylib_coll[i].path.c_str(), PATH_MAX); + // NULL terminate paths that are too long (redundant for path + // that fit, but harmless + info[i].name[PATH_MAX-1] = '\0'; + info[i].header_addr = dylib_coll[i].address; + info[i].state = (dylib_coll[i].address == INVALID_NUB_ADDRESS ? eShlibStateUnloaded : eShlibStateLoaded); + memcpy(info[i].uuid, dylib_coll[i].uuid, sizeof(uuid_t)); + info[i].num_segments = dylib_coll[i].segments.size(); + if (info[i].num_segments == 0) + { + info[i].segments = NULL; + } + else + { + info[i].segments = &all_segments[segment_index]; + memcpy(info[i].segments, &(dylib_coll[i].segments[0]), sizeof(DNBSegment) * info[i].num_segments); + segment_index += info[i].num_segments; + } + + } + // Release ownership of the shared library array to the caller + *image_infos = info; + return dylib_coll.size(); + } + } + *image_infos = NULL; + return 0; +} + + + +nub_size_t +MachDYLD::CopySharedInfoCallback(nub_process_t pid, struct DNBExecutableImageInfo **image_infos, nub_bool_t only_changed, void *baton) +{ + MachDYLD *dyld = (MachDYLD*) baton; + + if (only_changed) + return dyld->CopyChangedShlibInfo(image_infos); + else + return dyld->CopyCurrentShlibInfo(image_infos); + + *image_infos = NULL; + return 0; +} + +nub_size_t +MachDYLD::CopyCurrentShlibInfo(DNBExecutableImageInfo **image_infos) +{ + PThreadMutex::Locker locker(m_dyld_info_mutex); + return CopySharedLibraryInfo(m_current_dylibs, image_infos); +} + + +nub_size_t +MachDYLD::CopyChangedShlibInfo(DNBExecutableImageInfo **image_infos) +{ + PThreadMutex::Locker locker(m_dyld_info_mutex); + return CopySharedLibraryInfo(m_changed_dylibs, image_infos); +} + + + +void +MachDYLD::DYLIBInfo::Dump(FILE *f) const +{ + if (f == NULL) + return; + if (address == INVALID_NUB_ADDRESS) + { + if (UUIDValid()) + { + fprintf(f, "UNLOADED %8.8llx %2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X %s", + (uint64_t)mod_date, + uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3], + uuid[ 4], uuid[ 5], uuid[ 6], uuid[ 7], + uuid[ 8], uuid[ 9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15], + path.c_str()); + } + else + { + fprintf(f, "UNLOADED %8.8llx %s", (uint64_t)mod_date, path.c_str()); + } + } + else + { + if (UUIDValid()) + { + fprintf(f, "%8.8llx %8.8llx %2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X %s", + (uint64_t)address, + (uint64_t)mod_date, + uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3], + uuid[ 4], uuid[ 5], uuid[ 6], uuid[ 7], + uuid[ 8], uuid[ 9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15], + path.c_str()); + } + else + { + fprintf(f, "%8.8llx %8.8llx %s", (uint64_t)address, (uint64_t)mod_date, path.c_str()); + } + } +} + +void +MachDYLD::Dump(FILE *f) const +{ + if (f == NULL) + return; + + PThreadMutex::Locker locker(m_dyld_info_mutex); + fprintf(f, "\n\tMachDYLD.m_dylib_info_header: version=%d, count=%d, addr=0x%llx, notify=0x%llx", + m_dylib_info_header.version, + m_dylib_info_header.dylib_info_count, + (uint64_t)m_dylib_info_header.dylib_info_addr, + (uint64_t)m_dylib_info_header.notification); + uint32_t i; + fprintf(f, "\n\tMachDYLD.m_current_dylibs"); + for (i = 0; i<m_current_dylibs.size(); i++) + m_current_dylibs[i].Dump(f); + fprintf(f, "\n\tMachDYLD.m_changed_dylibs"); + for (i = 0; i<m_changed_dylibs.size(); i++) + m_changed_dylibs[i].Dump(f); +} + |