diff options
Diffstat (limited to 'lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp')
-rw-r--r-- | lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp | 1129 |
1 files changed, 1129 insertions, 0 deletions
diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp new file mode 100644 index 00000000000..a8dbf053e3d --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp @@ -0,0 +1,1129 @@ +//===-- DynamicLoaderMacOSXDYLD.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/StackFrame.h" + +#include "DynamicLoaderMacOSXDYLD.h" +#include "DynamicLoaderMacOSXDYLDLog.h" + +//#define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN +#ifdef ENABLE_DEBUG_PRINTF +#include <stdio.h> +#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +using namespace lldb; +using namespace lldb_private; + + +/// FIXME - The ObjC Runtime trampoline handler doesn't really belong here. +/// I am putting it here so I can invoke it in the Trampoline code here, but +/// it should be moved to the ObjC Runtime support when it is set up. + +//---------------------------------------------------------------------- +// Create an instance of this class. This function is filled into +// the plugin info class that gets handed out by the plugin factory and +// allows the lldb to instantiate an instance of this class. +//---------------------------------------------------------------------- +DynamicLoader * +DynamicLoaderMacOSXDYLD::CreateInstance (Process* process) +{ + return new DynamicLoaderMacOSXDYLD (process); +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DynamicLoaderMacOSXDYLD::DynamicLoaderMacOSXDYLD (Process* process) : + DynamicLoader(process), + m_dyld(), + m_dyld_all_image_infos_addr(LLDB_INVALID_ADDRESS), + m_dyld_all_image_infos(), + m_break_id(LLDB_INVALID_BREAK_ID), + m_dyld_image_infos(), + m_mutex(Mutex::eMutexTypeRecursive), + m_objc_trampoline_handler_ap(NULL) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoaderMacOSXDYLD::~DynamicLoaderMacOSXDYLD() +{ + Clear(true); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderMacOSXDYLD::DidAttach () +{ + PrivateInitialize(m_process); + if (NeedToLocateDYLD ()) + LocateDYLD (); + SetNotificationBreakpoint (); + UpdateAllImageInfos(); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderMacOSXDYLD::DidLaunch () +{ + PrivateInitialize(m_process); + if (NeedToLocateDYLD ()) + LocateDYLD (); + SetNotificationBreakpoint (); + UpdateAllImageInfos(); +} + + +//---------------------------------------------------------------------- +// Clear out the state of this class. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Clear (bool clear_process) +{ + Mutex::Locker locker(m_mutex); + + if (m_process->IsAlive() && LLDB_BREAK_ID_IS_VALID(m_break_id)) + m_process->ClearBreakpointSiteByID(m_break_id); + + if (clear_process) + m_process = NULL; + m_dyld.Clear(false); + m_dyld_all_image_infos_addr = LLDB_INVALID_ADDRESS; + m_dyld_all_image_infos.Clear(); + m_break_id = LLDB_INVALID_BREAK_ID; + m_dyld_image_infos.clear(); +} + +//---------------------------------------------------------------------- +// Check if we have found DYLD yet +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::DidSetNotificationBreakpoint() const +{ + return LLDB_BREAK_ID_IS_VALID (m_break_id); +} + +//---------------------------------------------------------------------- +// Try and figure out where dyld is by first asking the Process +// if it knows (which currently calls down in the the lldb::Process +// to get the DYLD info (available on SnowLeopard only). If that fails, +// then check in the default addresses. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::LocateDYLD() +{ + if (m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS) + m_dyld_all_image_infos_addr = m_process->GetImageInfoAddress (); + + if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) + { + if (ReadAllImageInfosStructure ()) + { + if (m_dyld_all_image_infos.dyldImageLoadAddress != LLDB_INVALID_ADDRESS) + return ReadDYLDInfoFromMemoryAndSetNotificationCallback (m_dyld_all_image_infos.dyldImageLoadAddress); + else + return ReadDYLDInfoFromMemoryAndSetNotificationCallback (m_dyld_all_image_infos_addr & 0xfffffffffff00000ull); + } + } + + // Check some default values + Module *executable = m_process->GetTarget().GetExecutableModule().get(); + + if (executable) + { + if (executable->GetArchitecture().GetAddressByteSize() == 8) + { + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x7fff5fc00000ull); + } +#if defined (__arm__) + else + { + ArchSpec arm_arch("arm"); + if (arm_arch == executable->Arch()) + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x2fe00000); + } +#endif + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x8fe00000); + } + return false; +} + +//---------------------------------------------------------------------- +// Assume that dyld is in memory at ADDR and try to parse it's load +// commands +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::ReadDYLDInfoFromMemoryAndSetNotificationCallback(lldb::addr_t addr) +{ + DataExtractor data; // Load command data + if (ReadMachHeader (addr, &m_dyld.header, &data)) + { + if (m_dyld.header.filetype == MH_DYLINKER) + { + m_dyld.address = addr; + ModuleSP dyld_module_sp; + if (ParseLoadCommands (data, m_dyld, &m_dyld.file_spec)) + { + if (m_dyld.file_spec) + { + ArchSpec dyld_arch(m_dyld.header.cputype, m_dyld.header.cpusubtype); + dyld_module_sp = m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (m_dyld.file_spec); + + if (dyld_module_sp.get() == NULL || dyld_module_sp->GetArchitecture() != dyld_arch) + { + dyld_module_sp = m_process->GetTarget().GetSharedModule (m_dyld.file_spec, + dyld_arch, + &m_dyld.uuid); + } + + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + } + } + + if (m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS && dyld_module_sp.get()) + { + static ConstString g_dyld_all_image_infos ("dyld_all_image_infos"); + const Symbol *symbol = dyld_module_sp->FindFirstSymbolWithNameAndType (g_dyld_all_image_infos, eSymbolTypeData); + if (symbol) + m_dyld_all_image_infos_addr = symbol->GetValue().GetLoadAddress(m_process); + } + + // Update all image infos + UpdateAllImageInfos(); + + // If we didn't have an executable before, but now we do, then the + // dyld module shared pointer might be unique and we may need to add + // it again (since Target::SetExecutableModule() will clear the + // images). So append the dyld module back to the list if it is + /// unique! + if (m_process->GetTarget().GetImages().AppendInNeeded (dyld_module_sp)) + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + + return true; + } + } + return false; +} + +bool +DynamicLoaderMacOSXDYLD::NeedToLocateDYLD () const +{ + return m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS; +} + +bool +DynamicLoaderMacOSXDYLD::UpdateCommPageLoadAddress(Module *module) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + uint32_t num_sections = section_list->GetSize(); + for (uint32_t i=0; i<num_sections; ++i) + { + Section* section = section_list->GetSectionAtIndex (i).get(); + if (section) + { + const addr_t new_section_load_addr = section->GetFileAddress (); + const addr_t old_section_load_addr = m_process->GetSectionLoadAddress (section); + if (old_section_load_addr == LLDB_INVALID_ADDRESS || + old_section_load_addr != new_section_load_addr) + { + if (m_process->SectionLoaded (section, section->GetFileAddress ())) + changed = true; + } + } + } + } + } + } + return changed; +} + +//---------------------------------------------------------------------- +// Update the load addresses for all segments in MODULE using the +// updated INFO that is passed in. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::UpdateImageLoadAddress (Module *module, struct DYLDImageInfo& info) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + // All sections listed in the dyld image info structure will all + // either be fixed up already, or they will all be off by a single + // slide amount that is determined by finding the first segment + // that is at file offset zero which also has bytes (a file size + // that is greater than zero) in the object file. + + // Determine the slide amount (if any) + info.slide = 0; + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + Section *section = section_list->GetSectionAtIndex (sect_idx).get(); + if (section) + { + // Find the first section that begins at file offset zero + // a file size (skip page zero). + if (section->GetFileOffset() == 0 && section->GetFileSize() > 0) + { + // We have now found the section, lets match it up + // with the section in the dyld image info structure. + const Segment *dyld_segment = info.FindSegment (section->GetName()); + if (dyld_segment) + info.slide = info.address - dyld_segment->addr; + // We have found the slide amount, so we can exit + // this for loop. + break; + } + } + } + + // We now know the slide amount, so go through all sections + // and update the load addresses with the correct values. + uint32_t num_segments = info.segments.size(); + for (uint32_t i=0; i<num_segments; ++i) + { + SectionSP section_sp(section_list->FindSectionByName(info.segments[i].name)); + assert (section_sp.get() != NULL); + const addr_t new_section_load_addr = info.segments[i].addr + info.slide; + const addr_t old_section_load_addr = m_process->GetSectionLoadAddress (section_sp.get()); + if (old_section_load_addr == LLDB_INVALID_ADDRESS || + old_section_load_addr != new_section_load_addr) + { + if (m_process->SectionLoaded (section_sp.get(), new_section_load_addr)) + changed = true; + } + } + } + } + } + return changed; +} + +//---------------------------------------------------------------------- +// Update the load addresses for all segments in MODULE using the +// updated INFO that is passed in. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::UnloadImageLoadAddress (Module *module, struct DYLDImageInfo& info) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + uint32_t num_segments = info.segments.size(); + for (uint32_t i=0; i<num_segments; ++i) + { + SectionSP section_sp(section_list->FindSectionByName(info.segments[i].name)); + assert (section_sp.get() != NULL); + const addr_t old_section_load_addr = info.segments[i].addr + info.slide; + if (m_process->SectionUnloaded (section_sp.get(), old_section_load_addr)) + changed = true; + } + } + } + } + return changed; +} + + +//---------------------------------------------------------------------- +// Static callback function that gets called when our DYLD notification +// breakpoint gets hit. We update all of our image infos and then +// let our super class DynamicLoader class decide if we should stop +// or not (based on global preference). +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::NotifyBreakpointHit (void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) +{ + // Let the event know that the images have changed + DynamicLoaderMacOSXDYLD* dyld_instance = (DynamicLoaderMacOSXDYLD*) baton; + dyld_instance->UpdateAllImageInfos(); + // Return true to stop the target, false to just let the target run + return dyld_instance->GetStopWhenImagesChange(); +} + +bool +DynamicLoaderMacOSXDYLD::ReadAllImageInfosStructure () +{ + Mutex::Locker locker(m_mutex); + m_dyld_all_image_infos.Clear(); + if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) + { + const ByteOrder endian = m_process->GetByteOrder(); + const uint32_t addr_size = m_process->GetAddressByteSize(); + uint8_t buf[256]; + const size_t count = 2 * sizeof(uint32_t) + // version + dylib_info_count + addr_size * 2 + // dylib_info_addr + notification + 2 + addr_size - 2 + // processDetachedFromSharedRegion + libSystemInitialized + pad + addr_size; // dyldImageLoadAddress + Error error; + const size_t bytes_read = m_process->ReadMemory (m_dyld_all_image_infos_addr, buf, count, error); + if (bytes_read == count) + { + DataExtractor data(buf, count, endian, addr_size); + uint32_t offset = 0; + m_dyld_all_image_infos.version = data.GetU32(&offset); + m_dyld_all_image_infos.dylib_info_count = data.GetU32(&offset); + m_dyld_all_image_infos.dylib_info_addr = data.GetPointer(&offset); + m_dyld_all_image_infos.notification = data.GetPointer(&offset); + m_dyld_all_image_infos.processDetachedFromSharedRegion = data.GetU8(&offset); + if (m_dyld_all_image_infos.version >= 2) + { + m_dyld_all_image_infos.libSystemInitialized = data.GetU8(&offset); + // Adjust for padding. + offset += addr_size - 2; + m_dyld_all_image_infos.dyldImageLoadAddress = data.GetPointer(&offset); + } + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// If we have found where the "_dyld_all_image_infos" lives in memory, +// read the current info from it, and then update all image load +// addresses (or lack thereof). +//---------------------------------------------------------------------- +uint32_t +DynamicLoaderMacOSXDYLD::UpdateAllImageInfos() +{ + if (ReadAllImageInfosStructure ()) + { + Mutex::Locker locker(m_mutex); + uint32_t idx; + Error error; + uint32_t i = 0; + DYLDImageInfo::collection old_dyld_all_image_infos; + old_dyld_all_image_infos.swap(m_dyld_image_infos); + + // If we made it here, we are assuming that the all dylib info data should + // be valid, lets read the info array. + const ByteOrder endian = m_process->GetByteOrder(); + const uint32_t addr_size = m_process->GetAddressByteSize(); + + if (m_dyld_all_image_infos.dylib_info_count > 0) + { + if (m_dyld_all_image_infos.dylib_info_addr == 0) + { + // DYLD is updating the images right now... + } + else + { + m_dyld_image_infos.resize(m_dyld_all_image_infos.dylib_info_count); + const size_t count = m_dyld_image_infos.size() * 3 * addr_size; + DataBufferHeap info_data(count, 0); + Error error; + const size_t bytes_read = m_process->ReadMemory (m_dyld_all_image_infos.dylib_info_addr, + info_data.GetBytes(), + info_data.GetByteSize(), + error); + if (bytes_read == count) + { + uint32_t info_data_offset = 0; + DataExtractor info_data_ref(info_data.GetBytes(), info_data.GetByteSize(), endian, addr_size); + for (i = 0; info_data_ref.ValidOffset(info_data_offset); i++) + { + assert (i < m_dyld_image_infos.size()); + m_dyld_image_infos[i].address = info_data_ref.GetPointer(&info_data_offset); + lldb::addr_t path_addr = info_data_ref.GetPointer(&info_data_offset); + m_dyld_image_infos[i].mod_date = info_data_ref.GetPointer(&info_data_offset); + + char raw_path[PATH_MAX]; + m_process->ReadMemory (path_addr, raw_path, sizeof(raw_path), error); + m_dyld_image_infos[i].file_spec.SetFile(raw_path); + } + assert(i == m_dyld_all_image_infos.dylib_info_count); + + UpdateAllImageInfosHeaderAndLoadCommands(); + } + else + { + DEBUG_PRINTF( "unable to read all data for all_dylib_infos."); + m_dyld_image_infos.clear(); + } + } + } + else + { + m_dyld_image_infos.clear(); + } + + // If our new list is smaller than our old list, we have unloaded + // some shared libraries + if (m_dyld_image_infos.size() < old_dyld_all_image_infos.size()) + { + ModuleList unloaded_module_list; + for (idx = m_dyld_image_infos.size(); idx < old_dyld_all_image_infos.size(); ++idx) + { + ModuleSP unload_image_module_sp(m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (old_dyld_all_image_infos[idx].file_spec)); + if (unload_image_module_sp.get()) + { + if (UnloadImageLoadAddress (unload_image_module_sp.get(), old_dyld_all_image_infos[idx])) + unloaded_module_list.AppendInNeeded (unload_image_module_sp); + } + } + if (unloaded_module_list.GetSize() > 0) + m_process->GetTarget().ModulesDidUnload (unloaded_module_list); + } + } + else + { + m_dyld_image_infos.clear(); + } + + const uint32_t num_dylibs = m_dyld_image_infos.size(); + if (num_dylibs > 0) + { + ModuleList loaded_module_list; + for (uint32_t idx = 0; idx<num_dylibs; ++idx) + { + ArchSpec arch_spec(m_dyld_image_infos[idx].header.cputype, m_dyld_image_infos[idx].header.cpusubtype); + ModuleSP image_module_sp(m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (m_dyld_image_infos[idx].file_spec)); + if (image_module_sp.get() == NULL || image_module_sp->GetArchitecture() != arch_spec) + { + image_module_sp = m_process->GetTarget().GetSharedModule (m_dyld_image_infos[idx].file_spec, + arch_spec, + &m_dyld_image_infos[idx].uuid); + } + + if (image_module_sp) + { + ObjectFile *objfile = image_module_sp->GetObjectFile (); + if (objfile) + { + SectionList *sections = objfile->GetSectionList(); + if (sections) + { + ConstString commpage_dbstr("__commpage"); + Section *commpage_section = sections->FindSectionByName(commpage_dbstr).get(); + if (commpage_section) + { + FileSpec objfile_file_spec(objfile->GetFileSpec()); + ModuleSP commpage_image_module_sp(m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (objfile_file_spec, &commpage_dbstr)); + if (commpage_image_module_sp.get() == NULL) + { + commpage_image_module_sp = m_process->GetTarget().GetSharedModule (m_dyld_image_infos[idx].file_spec, + arch_spec, + &m_dyld_image_infos[idx].uuid, + &commpage_dbstr, + objfile->GetOffset() + commpage_section->GetOffset()); + UpdateCommPageLoadAddress(commpage_image_module_sp.get()); + } + } + } + } + + // UpdateImageLoadAddress will return true if any segments + // change load address. We need to check this so we don't + // mention that all loaded shared libraries are newly loaded + // each time we hit out dyld breakpoint since dyld will list all + // shared libraries each time. + if (UpdateImageLoadAddress (image_module_sp.get(), m_dyld_image_infos[idx])) + { + loaded_module_list.AppendInNeeded (image_module_sp); + } + } + } + PutToLog(DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (1)); + if (loaded_module_list.GetSize() > 0) + { + // FIXME: This should really be in the Runtime handlers class, which should get + // called by the target's ModulesDidLoad, but we're doing it all locally for now + // to save time. + // Also, I'm assuming there can be only one libobjc dylib loaded... + + if (m_objc_trampoline_handler_ap.get() == NULL) + { + size_t num_modules = loaded_module_list.GetSize(); + for (int i = 0; i < num_modules; i++) + { + if (ObjCTrampolineHandler::ModuleIsObjCLibrary (loaded_module_list.GetModuleAtIndex (i))) + { + m_objc_trampoline_handler_ap.reset (new ObjCTrampolineHandler(m_process->GetSP(), loaded_module_list.GetModuleAtIndex (i))); + break; + } + } + } + m_process->GetTarget().ModulesDidLoad (loaded_module_list); + } + } + return m_dyld_image_infos.size(); +} + +//---------------------------------------------------------------------- +// Read a mach_header at ADDR into HEADER, and also fill in the load +// command data into LOAD_COMMAND_DATA if it is non-NULL. +// +// Returns true if we succeed, false if we fail for any reason. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::ReadMachHeader (lldb::addr_t addr, struct mach_header *header, DataExtractor *load_command_data) +{ + DataBufferHeap header_bytes(sizeof(struct mach_header), 0); + Error error; + size_t bytes_read = m_process->ReadMemory (addr, + header_bytes.GetBytes(), + header_bytes.GetByteSize(), + error); + if (bytes_read == sizeof(struct mach_header)) + { + uint32_t offset = 0; + ::memset (header, 0, sizeof(header)); + + // Get the magic byte unswapped so we can figure out what we are dealing with + DataExtractor data(header_bytes.GetBytes(), header_bytes.GetByteSize(), eByteOrderHost, 4); + header->magic = data.GetU32(&offset); + lldb::addr_t load_cmd_addr = addr; + data.SetByteOrder(DynamicLoaderMacOSXDYLD::GetByteOrderFromMagic(header->magic)); + switch (header->magic) + { + case MH_MAGIC: + case MH_CIGAM: + data.SetAddressByteSize(4); + load_cmd_addr += sizeof(struct mach_header); + break; + + case MH_MAGIC_64: + case MH_CIGAM_64: + data.SetAddressByteSize(8); + load_cmd_addr += sizeof(struct mach_header_64); + break; + + default: + return false; + } + + // Read the rest of dyld's mach header + if (data.GetU32(&offset, &header->cputype, (sizeof(struct mach_header)/sizeof(uint32_t)) - 1)) + { + if (load_command_data == NULL) + return true; // We were able to read the mach_header and weren't asked to read the load command bytes + + DataBufferSP load_cmd_data_sp(new DataBufferHeap(header->sizeofcmds, 0)); + + size_t load_cmd_bytes_read = m_process->ReadMemory (load_cmd_addr, + load_cmd_data_sp->GetBytes(), + load_cmd_data_sp->GetByteSize(), + error); + + if (load_cmd_bytes_read == header->sizeofcmds) + { + // Set the load command data and also set the correct endian + // swap settings and the correct address size + load_command_data->SetData(load_cmd_data_sp, 0, header->sizeofcmds); + load_command_data->SetByteOrder(data.GetByteOrder()); + load_command_data->SetAddressByteSize(data.GetAddressByteSize()); + return true; // We successfully read the mach_header and the load command data + } + + return false; // We weren't able to read the load command data + } + } + return false; // We failed the read the mach_header +} + + +//---------------------------------------------------------------------- +// Parse the load commands for an image +//---------------------------------------------------------------------- +uint32_t +DynamicLoaderMacOSXDYLD::ParseLoadCommands (const DataExtractor& data, struct DYLDImageInfo& dylib_info, FileSpec *lc_id_dylinker) +{ + uint32_t offset = 0; + uint32_t cmd_idx; + Segment segment; + dylib_info.Clear (true); + + for (cmd_idx = 0; cmd_idx < dylib_info.header.ncmds; cmd_idx++) + { + // Clear out any load command specific data from DYLIB_INFO since + // we are about to read it. + + if (data.ValidOffsetForDataOfSize (offset, sizeof(struct load_command))) + { + struct load_command load_cmd; + uint32_t load_cmd_offset = offset; + load_cmd.cmd = data.GetU32 (&offset); + load_cmd.cmdsize = data.GetU32 (&offset); + switch (load_cmd.cmd) + { + case LC_SEGMENT: + { + segment.name.SetTrimmedCStringWithLength ((const char *)data.GetData(&offset, 16), 16); + segment.addr = data.GetU32 (&offset); + segment.size = data.GetU32 (&offset); + dylib_info.segments.push_back (segment); + } + break; + + case LC_SEGMENT_64: + { + segment.name.SetTrimmedCStringWithLength ((const char *)data.GetData(&offset, 16), 16); + segment.addr = data.GetU64 (&offset); + segment.size = data.GetU64 (&offset); + dylib_info.segments.push_back (segment); + } + break; + + case LC_ID_DYLINKER: + if (lc_id_dylinker) + { + uint32_t name_offset = load_cmd_offset + data.GetU32 (&offset); + const char *path = data.PeekCStr (name_offset); + lc_id_dylinker->SetFile (path); + } + break; + + case LC_UUID: + dylib_info.uuid.SetBytes(data.GetData (&offset, 16)); + break; + + default: + break; + } + // Set offset to be the beginning of the next load command. + offset = load_cmd_offset + load_cmd.cmdsize; + } + } + return cmd_idx; +} + +//---------------------------------------------------------------------- +// Read the mach_header and load commands for each image that the +// _dyld_all_image_infos structure points to and cache the results. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::UpdateAllImageInfosHeaderAndLoadCommands() +{ + uint32_t exe_idx = UINT32_MAX; + // Read any UUID values that we can get + for (uint32_t i = 0; i < m_dyld_all_image_infos.dylib_info_count; i++) + { + if (!m_dyld_image_infos[i].UUIDValid()) + { + DataExtractor data; // Load command data + if (!ReadMachHeader (m_dyld_image_infos[i].address, &m_dyld_image_infos[i].header, &data)) + continue; + + ParseLoadCommands (data, m_dyld_image_infos[i], NULL); + + if (m_dyld_image_infos[i].header.filetype == MH_EXECUTE) + exe_idx = i; + } + } + + if (exe_idx < m_dyld_image_infos.size()) + { + bool set_executable = false; + ArchSpec dyld_exe_arch_spec(m_dyld_image_infos[exe_idx].header.cputype, m_dyld_image_infos[exe_idx].header.cpusubtype); + ModuleSP exe_module_sp(m_process->GetTarget().GetExecutableModule()); + if (exe_module_sp.get()) + { + if (exe_module_sp->GetFileSpec() != m_dyld_image_infos[exe_idx].file_spec || + exe_module_sp->GetArchitecture() != dyld_exe_arch_spec) + set_executable = true; + } + else + set_executable = true; + + if (set_executable) + { + exe_module_sp = m_process->GetTarget().GetSharedModule (m_dyld_image_infos[exe_idx].file_spec, + dyld_exe_arch_spec, + &m_dyld_image_infos[exe_idx].uuid); + if (exe_module_sp.get()) + { + // If we found the file where it purported to be, then it should + // be safe to load dependent images. + bool get_dependent_images = exe_module_sp->GetFileSpec() == m_dyld_image_infos[exe_idx].file_spec; + + m_process->GetTarget().SetExecutableModule (exe_module_sp, get_dependent_images); + } + } + } +} + +//---------------------------------------------------------------------- +// Dump a Segment to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Segment::PutToLog (Log *log, lldb::addr_t slide) const +{ + if (log) + log->Printf("\t\t%16s [0x%16.16llx - 0x%16.16llx)", name.AsCString(""), addr + slide, addr + slide + size); +} + +const DynamicLoaderMacOSXDYLD::Segment * +DynamicLoaderMacOSXDYLD::DYLDImageInfo::FindSegment (const ConstString &name) const +{ + const size_t num_segments = segments.size(); + for (size_t i=0; i<num_segments; ++i) + { + if (segments[i].name == name) + return &segments[i]; + } + return NULL; +} + + +//---------------------------------------------------------------------- +// Dump an image info structure to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::DYLDImageInfo::PutToLog (Log *log) const +{ + if (log == NULL) + return; + uint8_t *u = (uint8_t *)uuid.GetBytes(); + + if (address == LLDB_INVALID_ADDRESS) + { + if (u) + { + log->Printf("\t modtime=0x%8.8llx uuid=%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 path='%s/%s' (UNLOADED)", + mod_date, + u[ 0], u[ 1], u[ 2], u[ 3], + u[ 4], u[ 5], u[ 6], u[ 7], + u[ 8], u[ 9], u[10], u[11], + u[12], u[13], u[14], u[15], + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + } + else + log->Printf("\t modtime=0x%8.8llx path='%s/%s' (UNLOADED)", + mod_date, + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + } + else + { + if (u) + { + log->Printf("\taddress=0x%16.16llx modtime=0x%8.8llx uuid=%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 path='%s/%s'", + address, + mod_date, + u[ 0], u[ 1], u[ 2], u[ 3], + u[ 4], u[ 5], u[ 6], u[ 7], + u[ 8], u[ 9], u[10], u[11], + u[12], u[13], u[14], u[15], + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + } + else + { + log->Printf("\taddress=0x%16.16llx modtime=0x%8.8llx path='%s/%s'", + address, + mod_date, + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + + } + for (uint32_t i=0; i<segments.size(); ++i) + segments[i].PutToLog(log, slide); + } +} + +//---------------------------------------------------------------------- +// Dump the _dyld_all_image_infos members and all current image infos +// that we have parsed to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::PutToLog(Log *log) const +{ + if (log == NULL) + return; + + Mutex::Locker locker(m_mutex); + log->Printf("dyld_all_image_infos = { version=%d, count=%d, addr=0x%8.8llx, notify=0x%8.8llx }", + m_dyld_all_image_infos.version, + m_dyld_all_image_infos.dylib_info_count, + (uint64_t)m_dyld_all_image_infos.dylib_info_addr, + (uint64_t)m_dyld_all_image_infos.notification); + size_t i; + const size_t count = m_dyld_image_infos.size(); + if (count > 0) + { + log->Printf("\tdyld_image_infos"); + for (i = 0; i<count; i++) + m_dyld_image_infos[i].PutToLog(log); + } +} + +//---------------------------------------------------------------------- +// Static callback function that gets called when the process state +// changes. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Initialize(void *baton, Process *process) +{ + ((DynamicLoaderMacOSXDYLD*)baton)->PrivateInitialize(process); +} + +void +DynamicLoaderMacOSXDYLD::PrivateInitialize(Process *process) +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + Clear(true); + m_process = process; +} + + +//---------------------------------------------------------------------- +// Static callback function that gets called when the process state +// changes. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::ProcessStateChanged(void *baton, Process *process, StateType state) +{ + ((DynamicLoaderMacOSXDYLD*)baton)->PrivateProcessStateChanged(process, state); +} + +bool +DynamicLoaderMacOSXDYLD::SetNotificationBreakpoint () +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + if (m_break_id == LLDB_INVALID_BREAK_ID) + { + if (m_dyld_all_image_infos.notification != LLDB_INVALID_ADDRESS) + { + Address so_addr; + // Set the notification breakpoint and install a breakpoint + // callback function that will get called each time the + // breakpoint gets hit. We will use this to track when shared + // libraries get loaded/unloaded. + + if (m_process->ResolveLoadAddress(m_dyld_all_image_infos.notification, so_addr)) + { + Breakpoint *dyld_break = m_process->GetTarget().CreateBreakpoint (so_addr, true).get(); + dyld_break->SetCallback (DynamicLoaderMacOSXDYLD::NotifyBreakpointHit, this, true); + m_break_id = dyld_break->GetID(); + } + } + } + return m_break_id != LLDB_INVALID_BREAK_ID; +} + +//----------------------------------------------------------------------Target.h + +// Member function that gets called when the process state changes. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::PrivateProcessStateChanged (Process *process, StateType state) +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s(%s)\n", __FUNCTION__, StateAsCString(state)); + switch (state) + { + case eStateAttaching: + case eStateLaunching: + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + case eStateDetached: + Clear(false); + break; + + case eStateStopped: + // Keep trying find dyld and set our notification breakpoint each time + // we stop until we succeed + if (!DidSetNotificationBreakpoint () && m_process->IsAlive()) + { + if (NeedToLocateDYLD ()) + LocateDYLD (); + + SetNotificationBreakpoint (); + } + break; + + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + break; + + default: + break; + } +} + +ThreadPlanSP +DynamicLoaderMacOSXDYLD::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) +{ + ThreadPlanSP thread_plan_sp; + StackFrame *current_frame = thread.GetStackFrameAtIndex(0).get(); + const SymbolContext ¤t_context = current_frame->GetSymbolContext(eSymbolContextSymbol); + Symbol *current_symbol = current_context.symbol; + + if (current_symbol != NULL) + { + if (current_symbol->IsTrampoline()) + { + const ConstString &trampoline_name = current_symbol->GetMangled().GetName(); + if (trampoline_name) + { + SymbolContextList target_symbols; + ModuleList &images = thread.GetProcess().GetTarget().GetImages(); + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, target_symbols); + // FIXME - Make the Run to Address take multiple addresses, and + // run to any of them. + if (target_symbols.GetSize() == 1) + { + SymbolContext context; + AddressRange addr_range; + if (target_symbols.GetContextAtIndex(0, context)) + { + context.GetAddressRange (eSymbolContextEverything, addr_range); + thread_plan_sp.reset (new ThreadPlanRunToAddress (thread, addr_range.GetBaseAddress(), stop_others)); + } + } + else if (target_symbols.GetSize() > 1) + { + Log *log = DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (1); + if (log) + { + log->Printf ("Found more than one symbol for trampoline target: \"%s\"", trampoline_name.AsCString()); + } + } + else + { + Log *log = DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (1); + if (log) + { + log->Printf ("Could not find symbol for trampoline target: \"%s\"", trampoline_name.AsCString()); + } + } + } + } + } + + if (thread_plan_sp == NULL && m_objc_trampoline_handler_ap.get()) + thread_plan_sp = m_objc_trampoline_handler_ap->GetStepThroughDispatchPlan (thread, stop_others); + + return thread_plan_sp; +} + +void +DynamicLoaderMacOSXDYLD::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderMacOSXDYLD::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +DynamicLoaderMacOSXDYLD::GetPluginNameStatic() +{ + return "dynamic-loader.macosx-dyld"; +} + +const char * +DynamicLoaderMacOSXDYLD::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library loads/unloads in MacOSX user processes."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +DynamicLoaderMacOSXDYLD::GetPluginName() +{ + return "DynamicLoaderMacOSXDYLD"; +} + +const char * +DynamicLoaderMacOSXDYLD::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DynamicLoaderMacOSXDYLD::GetPluginVersion() +{ + return 1; +} + +void +DynamicLoaderMacOSXDYLD::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +DynamicLoaderMacOSXDYLD::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +DynamicLoaderMacOSXDYLD::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + |