diff options
Diffstat (limited to 'lldb/source/Plugins/DynamicLoader')
8 files changed, 2301 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; +} + + diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h new file mode 100644 index 00000000000..724a8b6bd6d --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h @@ -0,0 +1,360 @@ +//===-- DynamicLoaderMacOSXDYLD.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderMacOSXDYLD_h_ +#define liblldb_DynamicLoaderMacOSXDYLD_h_ + +// C Includes +#include <mach-o/loader.h> + +// C++ Includes +#include <map> +#include <vector> +#include <string> + +// Other libraries and framework includes +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "ObjCTrampolineHandler.h" + +class DynamicLoaderMacOSXDYLD : public lldb_private::DynamicLoader +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance (lldb_private::Process *process); + + DynamicLoaderMacOSXDYLD (lldb_private::Process *process); + + virtual + ~DynamicLoaderMacOSXDYLD (); + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + virtual void + DidAttach (); + + virtual void + DidLaunch (); + + //------------------------------------------------------------------ + // Process::Notifications callback functions + //------------------------------------------------------------------ + static void + Initialize (void *baton, + lldb_private::Process *process); + + static void + ProcessStateChanged (void *baton, + lldb_private::Process *process, + lldb::StateType state); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan (lldb_private::Thread &thread, + bool stop_others); + + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + + +protected: + void + PrivateInitialize (lldb_private::Process *process); + + void + PrivateProcessStateChanged (lldb_private::Process *process, + lldb::StateType state); + bool + LocateDYLD (); + + bool + DidSetNotificationBreakpoint () const; + + void + Clear (bool clear_process); + + void + PutToLog (lldb_private::Log *log) const; + + bool + ReadDYLDInfoFromMemoryAndSetNotificationCallback (lldb::addr_t addr); + + uint32_t + UpdateAllImageInfos (); + + static bool + NotifyBreakpointHit (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + void + UpdateAllImageInfosHeaderAndLoadCommands (); + + bool + UpdateCommPageLoadAddress (lldb_private::Module *module); + + uint32_t + AddrByteSize() + { + switch (m_dyld.header.magic) + { + case MH_MAGIC: + case MH_CIGAM: + return 4; + + case MH_MAGIC_64: + case MH_CIGAM_64: + return 8; + + default: + break; + } + return 0; + } + + static lldb::ByteOrder + GetByteOrderFromMagic (uint32_t magic) + { + switch (magic) + { + case MH_MAGIC: + case MH_MAGIC_64: + return lldb::eByteOrderHost; + + case MH_CIGAM: + case MH_CIGAM_64: + if (lldb::eByteOrderHost == lldb::eByteOrderBig) + return lldb::eByteOrderLittle; + else + return lldb::eByteOrderBig; + + default: + break; + } + return lldb::eByteOrderInvalid; + } + + bool + ReadMachHeader (lldb::addr_t addr, + struct mach_header *header, + lldb_private::DataExtractor *load_command_data); + class Segment + { + public: + + Segment() : + name(), + addr(LLDB_INVALID_ADDRESS), + size(0) + { + } + + lldb_private::ConstString name; + lldb::addr_t addr; + lldb::addr_t size; + + bool + operator==(const Segment& rhs) const + { + return name == rhs.name && addr == rhs.addr && size == rhs.size; + } + + void + PutToLog (lldb_private::Log *log, + lldb::addr_t slide) const; + + }; + + struct DYLDImageInfo + { + lldb::addr_t address; // Address of mach header for this dylib + lldb::addr_t slide; // The amount to slide all segments by if there is a global slide. + lldb::addr_t mod_date; // Modification date for this dylib + lldb_private::FileSpec file_spec; // Resolved path for this dylib + lldb_private::UUID uuid; // UUID for this dylib if it has one, else all zeros + struct mach_header header; // The mach header for this image + std::vector<Segment> segments; // All segment vmaddr and vmsize pairs for this executable (from memory of inferior) + + DYLDImageInfo() : + address(LLDB_INVALID_ADDRESS), + slide(0), + mod_date(0), + file_spec(), + uuid(), + header(), + segments() + { + } + + void + Clear(bool load_cmd_data_only) + { + if (!load_cmd_data_only) + { + address = LLDB_INVALID_ADDRESS; + slide = 0; + mod_date = 0; + file_spec.Clear(); + ::bzero (&header, sizeof(header)); + } + uuid.Clear(); + segments.clear(); + } + + bool + operator == (const DYLDImageInfo& rhs) const + { + return address == rhs.address + && slide == rhs.slide + && mod_date == rhs.mod_date + && file_spec == rhs.file_spec + && uuid == rhs.uuid + && memcmp(&header, &rhs.header, sizeof(header)) == 0 + && segments == rhs.segments; + } + + bool + UUIDValid() const + { + return uuid.IsValid(); + } + + const Segment * + FindSegment (const lldb_private::ConstString &name) const; + + void + PutToLog (lldb_private::Log *log) const; + + typedef std::vector<DYLDImageInfo> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + + struct DYLDAllImageInfos + { + uint32_t version; + uint32_t dylib_info_count; // Version >= 1 + lldb::addr_t dylib_info_addr; // Version >= 1 + lldb::addr_t notification; // Version >= 1 + bool processDetachedFromSharedRegion; // Version >= 1 + bool libSystemInitialized; // Version >= 2 + lldb::addr_t dyldImageLoadAddress; // Version >= 2 + + DYLDAllImageInfos() : + version (0), + dylib_info_count (0), + dylib_info_addr (LLDB_INVALID_ADDRESS), + notification (LLDB_INVALID_ADDRESS), + processDetachedFromSharedRegion (false), + libSystemInitialized (false), + dyldImageLoadAddress (LLDB_INVALID_ADDRESS) + { + } + + void + Clear() + { + version = 0; + dylib_info_count = 0; + dylib_info_addr = LLDB_INVALID_ADDRESS; + notification = LLDB_INVALID_ADDRESS; + processDetachedFromSharedRegion = false; + libSystemInitialized = false; + dyldImageLoadAddress = LLDB_INVALID_ADDRESS; + } + + bool + IsValid() const + { + return version >= 1 || version <= 6; + } + }; + + void + RegisterNotificationCallbacks(); + + void + UnregisterNotificationCallbacks(); + + uint32_t + ParseLoadCommands (const lldb_private::DataExtractor& data, + struct DYLDImageInfo& dylib_info, + lldb_private::FileSpec *lc_id_dylinker); + + bool + UpdateImageLoadAddress(lldb_private::Module *module, + struct DYLDImageInfo& info); + + bool + UnloadImageLoadAddress (lldb_private::Module *module, + struct DYLDImageInfo& info); + + bool + NeedToLocateDYLD () const; + + bool + SetNotificationBreakpoint (); + + bool + ReadAllImageInfosStructure (); + + DYLDImageInfo m_dyld; // Info about the curent dyld being used + lldb::addr_t m_dyld_all_image_infos_addr; + DYLDAllImageInfos m_dyld_all_image_infos; + lldb::user_id_t m_break_id; + DYLDImageInfo::collection m_dyld_image_infos; // Current shared libraries information + mutable lldb_private::Mutex m_mutex; + lldb_private::Process::Notifications m_notification_callbacks; + std::auto_ptr<lldb_private::ObjCTrampolineHandler> m_objc_trampoline_handler_ap; + +private: + DISALLOW_COPY_AND_ASSIGN (DynamicLoaderMacOSXDYLD); +}; + +#endif // liblldb_DynamicLoaderMacOSXDYLD_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.cpp new file mode 100644 index 00000000000..946c8f9310c --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.cpp @@ -0,0 +1,72 @@ +//===-- DynamicLoaderMacOSXDYLDLog.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DynamicLoaderMacOSXDYLDLog.h" +#include "lldb/Core/Log.h" + +using namespace lldb_private; + +static Log * +LogAccessor (bool get, Log *log) +{ + static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up + // by global constructors before other threads + // were done with it. + if (get) + { +// // Debug code below for enabling logging by default +// if (g_log == NULL) +// { +// g_log = new Log("/dev/stdout", false); +// g_log->GetMask().SetAllFlagBits(0xffffffffu); +// g_log->GetOptions().Set(LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_THREAD_NAME); +// } + } + else + { + if (g_log) + delete g_log; + g_log = log; + } + + return g_log; +} + +Log * +DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log = LogAccessor (true, NULL); + if (log && mask) + { + uint32_t log_mask = log->GetMask().GetAllFlagBits(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +DynamicLoaderMacOSXDYLDLog::SetLog (Log *log) +{ + LogAccessor (false, log); +} + + +void +DynamicLoaderMacOSXDYLDLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log = DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h new file mode 100644 index 00000000000..9282ba57647 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h @@ -0,0 +1,34 @@ +//===-- DynamicLoaderMacOSXDYLDLog.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderMacOSXDYLDLog_h_ +#define liblldb_DynamicLoaderMacOSXDYLDLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +#include "lldb/lldb-private.h" + +// Project includes + +class DynamicLoaderMacOSXDYLDLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet (uint32_t mask); + + static void + SetLog (lldb_private::Log *log); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_DynamicLoaderMacOSXDYLDLog_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.cpp new file mode 100644 index 00000000000..169dc89c791 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.cpp @@ -0,0 +1,328 @@ +//===-- ObjCTrampolineHandler.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjCTrampolineHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/ExecutionContext.h" +#include "ThreadPlanStepThroughObjCTrampoline.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" + +using namespace lldb; +using namespace lldb_private; + +const ObjCTrampolineHandler::DispatchFunction +ObjCTrampolineHandler::g_dispatch_functions[] = +{ + // NAME STRET SUPER FIXUP TYPE + {"objc_msgSend", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_fixup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_fixedup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSend_stret", true, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_stret_fixup", true, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_stret_fixedup", true, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSend_fpret", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_fpret_fixup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_fpret_fixedup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSend_fp2ret", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_fp2ret_fixup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_fp2ret_fixedup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSendSuper", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper_stret", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper2", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper2_fixup", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSendSuper2_fixedup", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSendSuper2_stret", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper2_stret_fixup", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSendSuper2_stret_fixedup", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {NULL} +}; + +bool +ObjCTrampolineHandler::ModuleIsObjCLibrary (const ModuleSP &module_sp) +{ + const FileSpec &module_file_spec = module_sp->GetFileSpec(); + static ConstString ObjCName ("libobjc.A.dylib"); + + if (module_file_spec) + { + if (module_file_spec.GetFilename() == ObjCName) + return true; + } + + return false; +} + +ObjCTrampolineHandler::ObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module) : + m_process_sp (process_sp), + m_objc_module_sp (objc_module), + m_impl_fn_addr (LLDB_INVALID_ADDRESS), + m_impl_stret_fn_addr (LLDB_INVALID_ADDRESS) +{ + // Look up the known resolution functions: + + ConstString get_impl_name("class_getMethodImplementation"); + ConstString get_impl_stret_name("class_getMethodImplementation_stret"); + + const Symbol *class_getMethodImplementation = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_name, eSymbolTypeCode); + const Symbol *class_getMethodImplementation_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_stret_name, eSymbolTypeCode); + + if (class_getMethodImplementation) + m_impl_fn_addr = class_getMethodImplementation->GetValue().GetLoadAddress(m_process_sp.get()); + if (class_getMethodImplementation_stret) + m_impl_stret_fn_addr = class_getMethodImplementation_stret->GetValue().GetLoadAddress(m_process_sp.get()); + + // FIXME: Do some kind of logging here. + if (m_impl_fn_addr == LLDB_INVALID_ADDRESS || m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS) + return; + + // Look up the addresses for the objc dispatch functions and cache them. For now I'm inspecting the symbol + // names dynamically to figure out how to dispatch to them. If it becomes more complicated than this we can + // turn the g_dispatch_functions char * array into a template table, and populate the DispatchFunction map + // from there. + + for (int i = 0; g_dispatch_functions[i].name != NULL; i++) + { + ConstString name_const_str(g_dispatch_functions[i].name); + const Symbol *msgSend_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (name_const_str, eSymbolTypeCode); + if (msgSend_symbol) + { + // FixMe: Make g_dispatch_functions static table of DisptachFunctions, and have the map be address->index. + // Problem is we also need to lookup the dispatch function. For now we could have a side table of stret & non-stret + // dispatch functions. If that's as complex as it gets, we're fine. + + lldb::addr_t sym_addr = msgSend_symbol->GetValue().GetLoadAddress(m_process_sp.get()); + + m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i)); + } + } +} + +ThreadPlanSP +ObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool stop_others) +{ + ThreadPlanSP ret_plan_sp; + lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); + + MsgsendMap::iterator pos; + pos = m_msgSend_map.find (curr_pc); + if (pos != m_msgSend_map.end()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + const DispatchFunction *this_dispatch = &g_dispatch_functions[(*pos).second]; + + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + + Process *process = thread.CalculateProcess(); + const ABI *abi = process->GetABI(); + if (abi == NULL) + return ret_plan_sp; + + Target *target = thread.CalculateTarget(); + + // FIXME: Since neither the value nor the Clang QualType know their ASTContext, + // we have to make sure the type we put in our value list comes from the same ASTContext + // the ABI will use to get the argument values. THis is the bottom-most frame's module. + + ClangASTContext *clang_ast_context = target->GetScratchClangASTContext(); + ValueList argument_values; + Value input_value; + void *clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false); + input_value.SetValueType (Value::eValueTypeScalar); + input_value.SetContext (Value::eContextTypeOpaqueClangQualType, clang_void_ptr_type); + + int obj_index; + int sel_index; + + // If this is a struct return dispatch, then the first argument is the + // return struct pointer, and the object is the second, and the selector is the third. + // Otherwise the object is the first and the selector the second. + if (this_dispatch->stret_return) + { + obj_index = 1; + sel_index = 2; + argument_values.PushValue(input_value); + argument_values.PushValue(input_value); + argument_values.PushValue(input_value); + } + else + { + obj_index = 0; + sel_index = 1; + argument_values.PushValue(input_value); + argument_values.PushValue(input_value); + } + + + bool success = abi->GetArgumentValues (thread, argument_values); + if (!success) + return ret_plan_sp; + + // Okay, the first value here is the object, we actually want the class of that object. + // For now we're just going with the ISA. + // FIXME: This should really be the return value of [object class] to properly handle KVO interposition. + + Value isa_value(*(argument_values.GetValueAtIndex(obj_index))); + + // This is a little cheesy, but since object->isa is the first field, + // making the object value a load address value and resolving it will get + // the pointer sized data pointed to by that value... + ExecutionContext exec_ctx; + thread.Calculate (exec_ctx); + + isa_value.SetValueType(Value::eValueTypeLoadAddress); + isa_value.ResolveValue(&exec_ctx, clang_ast_context->getASTContext()); + + if (this_dispatch->fixedup == DispatchFunction::eFixUpFixed) + { + // For the FixedUp method the Selector is actually a pointer to a + // structure, the second field of which is the selector number. + Value *sel_value = argument_values.GetValueAtIndex(sel_index); + sel_value->GetScalar() += process->GetAddressByteSize(); + sel_value->SetValueType(Value::eValueTypeLoadAddress); + sel_value->ResolveValue(&exec_ctx, clang_ast_context->getASTContext()); + } + else if (this_dispatch->fixedup == DispatchFunction::eFixUpToFix) + { + // FIXME: If the method dispatch is not "fixed up" then the selector is actually a + // pointer to the string name of the selector. We need to look that up... + // For now I'm going to punt on that and just return no plan. + if (log) + log->Printf ("Punting on stepping into un-fixed-up method dispatch."); + return ret_plan_sp; + } + + // FIXME: If this is a dispatch to the super-class, we need to get the super-class from + // the class, and disaptch to that instead. + // But for now I just punt and return no plan. + if (this_dispatch->is_super) + { + if (log) + log->Printf ("Punting on stepping into super method dispatch."); + return ret_plan_sp; + } + + ValueList dispatch_values; + dispatch_values.PushValue (isa_value); + dispatch_values.PushValue(*(argument_values.GetValueAtIndex(sel_index))); + + if (log) + { + log->Printf("Resolving method call for class - 0x%llx and selector - 0x%llx", + dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong()); + } + + lldb::addr_t impl_addr = LookupInCache (dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong()); + + if (impl_addr == LLDB_INVALID_ADDRESS) + { + + Address resolve_address(NULL, this_dispatch->stret_return ? m_impl_stret_fn_addr : m_impl_fn_addr); + + StreamString errors; + { + // Scope for mutex locker: + Mutex::Locker (m_impl_function_mutex); + if (!m_impl_function.get()) + { + m_impl_function.reset(new ClangFunction(process->GetTargetTriple().GetCString(), + clang_ast_context, + clang_void_ptr_type, + resolve_address, + dispatch_values)); + + unsigned num_errors = m_impl_function->CompileFunction(errors); + if (num_errors) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf ("Error compiling function: \"%s\".", errors.GetData()); + return ret_plan_sp; + } + + errors.Clear(); + if (!m_impl_function->WriteFunctionWrapper(exec_ctx, errors)) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf ("Error Inserting function: \"%s\".", errors.GetData()); + return ret_plan_sp; + } + } + + } + + errors.Clear(); + + // Now write down the argument values for this call. + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + if (!m_impl_function->WriteFunctionArguments (exec_ctx, args_addr, resolve_address, dispatch_values, errors)) + return ret_plan_sp; + + ret_plan_sp.reset (new ThreadPlanStepThroughObjCTrampoline (thread, this, args_addr, + argument_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong(), + stop_others)); + } + else + { + if (log) + log->Printf ("Found implementation address in cache: 0x%llx", impl_addr); + + ret_plan_sp.reset (new ThreadPlanRunToAddress (thread, impl_addr, stop_others)); + } + } + + return ret_plan_sp; +} + +void +ObjCTrampolineHandler::AddToCache (lldb::addr_t class_addr, lldb::addr_t selector, lldb::addr_t impl_addr) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + { + log->Printf ("Caching: class 0x%llx selector 0x%llx implementation 0x%llx.", class_addr, selector, impl_addr); + } + m_impl_cache.insert (std::pair<ClassAndSel,lldb::addr_t> (ClassAndSel(class_addr, selector), impl_addr)); +} + +lldb::addr_t +ObjCTrampolineHandler::LookupInCache (lldb::addr_t class_addr, lldb::addr_t selector) +{ + MsgImplMap::iterator pos, end = m_impl_cache.end(); + pos = m_impl_cache.find (ClassAndSel(class_addr, selector)); + if (pos != end) + return (*pos).second; + return LLDB_INVALID_ADDRESS; +} + +ClangFunction * +ObjCTrampolineHandler::GetLookupImplementationWrapperFunction () +{ + return m_impl_function.get(); +} diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.h new file mode 100644 index 00000000000..bc06d267d2f --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.h @@ -0,0 +1,133 @@ +//===-- ObjCTrampolineHandler.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_ObjCTrampolineHandler_h_ +#define lldb_ObjCTrampolineHandler_h_ + +// C Includes +// C++ Includes +#include <map> +#include <string> +// Other libraries and framework includes +// Project includes +#include "lldb.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Host/Mutex.h" + + +namespace lldb_private +{ +using namespace lldb; + +class ObjCTrampolineHandler { +public: + + ObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module_sp); + + ~ObjCTrampolineHandler() {} + + static bool ModuleIsObjCLibrary (const ModuleSP &module_sp); + + ThreadPlanSP + GetStepThroughDispatchPlan (Thread &thread, bool stop_others); + + void + AddToCache (lldb::addr_t class_addr, lldb::addr_t sel, lldb::addr_t impl_addr); + + lldb::addr_t + LookupInCache (lldb::addr_t class_addr, lldb::addr_t sel); + + ClangFunction * + GetLookupImplementationWrapperFunction (); + + + struct DispatchFunction { + public: + typedef enum + { + eFixUpNone, + eFixUpFixed, + eFixUpToFix + } FixUpState; + + const char *name; + bool stret_return; + bool is_super; + FixUpState fixedup; + }; + +private: + static const DispatchFunction g_dispatch_functions[]; + + typedef std::map<lldb::addr_t, int> MsgsendMap; // This table maps an dispatch fn address to the index in g_dispatch_functions + MsgsendMap m_msgSend_map; + ProcessSP m_process_sp; + ModuleSP m_objc_module_sp; + lldb::addr_t get_impl_addr; + std::auto_ptr<ClangFunction> m_impl_function; + Mutex m_impl_function_mutex; + lldb::addr_t m_impl_fn_addr; + lldb::addr_t m_impl_stret_fn_addr; + + + // We keep a map of <Class,Selector>->Implementation so we don't have to call the resolver + // function over and over. + + // FIXME: We need to watch for the loading of Protocols, and flush the cache for any + // class that we see so changed. + + struct ClassAndSel + { + ClassAndSel() + { + sel_addr = LLDB_INVALID_ADDRESS; + class_addr = LLDB_INVALID_ADDRESS; + } + ClassAndSel (lldb::addr_t in_sel_addr, lldb::addr_t in_class_addr) : + class_addr (in_class_addr), + sel_addr(in_sel_addr) + { + } + bool operator== (const ClassAndSel &rhs) + { + if (class_addr == rhs.class_addr + && sel_addr == rhs.sel_addr) + return true; + else + return false; + } + + bool operator< (const ClassAndSel &rhs) const + { + if (class_addr < rhs.class_addr) + return true; + else if (class_addr > rhs.class_addr) + return false; + else + { + if (sel_addr < rhs.sel_addr) + return true; + else + return false; + } + } + + lldb::addr_t class_addr; + lldb::addr_t sel_addr; + }; + + typedef std::map<ClassAndSel,lldb::addr_t> MsgImplMap; + MsgImplMap m_impl_cache; + +}; + +}; // using namespace lldb_private + +#endif // lldb_ObjCTrampolineHandler_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.cpp new file mode 100644 index 00000000000..a0cb0a86f18 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.cpp @@ -0,0 +1,151 @@ +//===-- ThreadPlanStepThroughObjCTrampoline.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "ThreadPlanStepThroughObjCTrampoline.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Core/Log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepThroughObjCTrampoline constructor +//---------------------------------------------------------------------- +ThreadPlanStepThroughObjCTrampoline::ThreadPlanStepThroughObjCTrampoline( + Thread &thread, + ObjCTrampolineHandler *trampoline_handler, + lldb::addr_t args_addr, + lldb::addr_t object_ptr, + lldb::addr_t class_ptr, + lldb::addr_t sel_ptr, + bool stop_others) : + ThreadPlan ("MacOSX Step through ObjC Trampoline", thread, eVoteNoOpinion, eVoteNoOpinion), + m_objc_trampoline_handler (trampoline_handler), + m_impl_function (trampoline_handler->GetLookupImplementationWrapperFunction()), + m_args_addr (args_addr), + m_object_ptr (object_ptr), + m_class_ptr (class_ptr), + m_sel_ptr (sel_ptr), + m_stop_others (stop_others) +{ + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ThreadPlanStepThroughObjCTrampoline::~ThreadPlanStepThroughObjCTrampoline() +{ +} + +void +ThreadPlanStepThroughObjCTrampoline::DidPush () +{ + StreamString errors; + ExecutionContext exc_context; + m_thread.Calculate(exc_context); + m_func_sp.reset(m_impl_function->GetThreadPlanToCallFunction (exc_context, m_args_addr, errors, m_stop_others)); + m_func_sp->SetPrivate(true); + m_thread.QueueThreadPlan (m_func_sp, false); +} + +void +ThreadPlanStepThroughObjCTrampoline::GetDescription (Stream *s, + lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf("Step through ObjC trampoline"); + else + { + s->Printf ("Stepping to implementation of ObjC method - obj: 0x%llx class: 0x%llx selector: 0x%llx", + m_object_ptr, m_class_ptr, m_sel_ptr); + } +} + +bool +ThreadPlanStepThroughObjCTrampoline::ValidatePlan (Stream *error) +{ + return true; +} + +bool +ThreadPlanStepThroughObjCTrampoline::PlanExplainsStop () +{ + // This plan should actually never stop when it is on the top of the plan + // stack, since it does all it's running in client plans. + return false; +} + +lldb::StateType +ThreadPlanStepThroughObjCTrampoline::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanStepThroughObjCTrampoline::ShouldStop (Event *event_ptr) +{ + if (m_func_sp.get() == NULL || m_thread.IsThreadPlanDone(m_func_sp.get())) + { + m_func_sp.reset(); + if (!m_run_to_sp) + { + Value target_addr_value; + ExecutionContext exc_context; + m_thread.Calculate(exc_context); + m_impl_function->FetchFunctionResults (exc_context, m_args_addr, target_addr_value); + m_impl_function->DeallocateFunctionResults(exc_context, m_args_addr); + lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong(); + Address target_address(NULL, target_addr); + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Running to ObjC method implementation: 0x%llx", target_addr); + + m_objc_trampoline_handler->AddToCache (m_class_ptr, m_sel_ptr, target_addr); + + // Extract the target address from the value: + + m_run_to_sp.reset(new ThreadPlanRunToAddress(m_thread, target_address, m_stop_others)); + m_thread.QueueThreadPlan(m_run_to_sp, false); + m_run_to_sp->SetPrivate(true); + return false; + } + else if (m_thread.IsThreadPlanDone(m_run_to_sp.get())) + { + SetPlanComplete(); + return true; + } + } + return false; +} + +// The base class MischiefManaged does some cleanup - so you have to call it +// in your MischiefManaged derived class. +bool +ThreadPlanStepThroughObjCTrampoline::MischiefManaged () +{ + if (IsPlanComplete()) + return true; + else + return false; +} + +bool +ThreadPlanStepThroughObjCTrampoline::WillStop() +{ + return true; +} diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.h new file mode 100644 index 00000000000..8033718277e --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.h @@ -0,0 +1,94 @@ +//===-- ThreadPlanStepThroughObjCTrampoline.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_ThreadPlanStepThroughObjCTrampoline_h_ +#define lldb_ThreadPlanStepThroughObjCTrampoline_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb-types.h" +#include "lldb-enumerations.h" +#include "lldb/Target/ThreadPlan.h" +#include "ObjCTrampolineHandler.h" + +namespace lldb_private +{ + +class ThreadPlanStepThroughObjCTrampoline : public ThreadPlan +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadPlanStepThroughObjCTrampoline(Thread &thread, + ObjCTrampolineHandler *trampoline_handler, + lldb::addr_t args_addr, + lldb::addr_t object_ptr, + lldb::addr_t class_ptr, + lldb::addr_t sel_ptr, + bool stop_others); + + virtual ~ThreadPlanStepThroughObjCTrampoline(); + + virtual void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + PlanExplainsStop (); + + + virtual lldb::StateType + RunState (); + + virtual bool + ShouldStop (Event *event_ptr); + + // The base class MischiefManaged does some cleanup - so you have to call it + // in your MischiefManaged derived class. + virtual bool + MischiefManaged (); + + virtual void + DidPush(); + + virtual bool + WillStop(); + + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ThreadPlanStepThroughObjCTrampoline can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For ThreadPlanStepThroughObjCTrampoline only + //------------------------------------------------------------------ + ThreadPlanSP m_func_sp; // This is the function call plan. We fill it at start, then set it + // to NULL when this plan is done. That way we know to go to: + lldb::addr_t m_args_addr; // Stores the address for our step through function result structure. + ThreadPlanSP m_run_to_sp; // The plan that runs to the target. + bool m_stop_others; + ObjCTrampolineHandler *m_objc_trampoline_handler; + ClangFunction *m_impl_function; // This is a pointer to a impl function that + // is owned by the client that pushes this plan. + lldb::addr_t m_object_ptr; + lldb::addr_t m_class_ptr; + lldb::addr_t m_sel_ptr; +}; + +}; // namespace lldb_private +#endif // lldb_ThreadPlanStepThroughObjCTrampoline_h_ |