diff options
-rw-r--r-- | lldb/lib/Makefile | 3 | ||||
-rw-r--r-- | lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.cpp | 191 | ||||
-rw-r--r-- | lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.h | 97 | ||||
-rw-r--r-- | lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.cpp | 315 | ||||
-rw-r--r-- | lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.h | 227 | ||||
-rw-r--r-- | lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.cpp | 371 | ||||
-rw-r--r-- | lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.h | 165 | ||||
-rw-r--r-- | lldb/source/Plugins/DynamicLoader/Linux-DYLD/Makefile | 14 | ||||
-rw-r--r-- | lldb/source/Plugins/Makefile | 2 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessLinux.cpp | 27 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessLinux.h | 12 | ||||
-rw-r--r-- | lldb/source/lldb.cpp | 3 |
12 files changed, 1425 insertions, 2 deletions
diff --git a/lldb/lib/Makefile b/lldb/lib/Makefile index 881b23c6185..abd9c1d8328 100644 --- a/lldb/lib/Makefile +++ b/lldb/lib/Makefile @@ -67,7 +67,8 @@ ifeq ($(HOST_OS),Darwin) endif ifeq ($(HOST_OS),Linux) - USEDLIBS += lldbPluginProcessLinux.a + USEDLIBS += lldbPluginProcessLinux.a \ + lldbPluginDynamicLoaderLinux.a endif include $(LEVEL)/Makefile.common diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.cpp b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.cpp new file mode 100644 index 00000000000..6954e4cac4d --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.cpp @@ -0,0 +1,191 @@ +//===-- AuxVector.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 +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" + +#include "AuxVector.h" + +using namespace lldb; +using namespace lldb_private; + +static bool +GetMaxU64(DataExtractor &data, + uint32_t *offset, uint64_t *value, unsigned int byte_size) +{ + uint32_t saved_offset = *offset; + *value = data.GetMaxU64(offset, byte_size); + return *offset != saved_offset; +} + +static bool +ParseAuxvEntry(DataExtractor &data, AuxVector::Entry &entry, + uint32_t *offset, unsigned int byte_size) +{ + if (!GetMaxU64(data, offset, &entry.type, byte_size)) + return false; + + if (!GetMaxU64(data, offset, &entry.value, byte_size)) + return false; + + return true; +} + +DataBufferSP +AuxVector::GetAuxvData() +{ + static const size_t path_size = 128; + static char path[path_size]; + DataBufferSP buf_sp; + int fd; + + // Ideally, we would simply create a FileSpec and call ReadFileContents. + // However, files in procfs have zero size (since they are, in general, + // dynamically generated by the kernel) which is incompatible with the + // current ReadFileContents implementation. Therefore we simply stream the + // data into a DataBuffer ourselves. + if (snprintf(path, path_size, "/proc/%d/auxv", m_process->GetID()) < 0) + return buf_sp; + + if ((fd = open(path, O_RDONLY, 0)) < 0) + return buf_sp; + + size_t bytes_read = 0; + std::auto_ptr<DataBufferHeap> buf_ap(new DataBufferHeap(1024, 0)); + for (;;) + { + size_t avail = buf_ap->GetByteSize() - bytes_read; + ssize_t status = read(fd, buf_ap->GetBytes() + bytes_read, avail); + + if (status < 0) + break; + + bytes_read += status; + + if (status == 0) + { + buf_ap->SetByteSize(bytes_read); + buf_sp.reset(buf_ap.release()); + break; + } + + if (avail - status == 0) + buf_ap->SetByteSize(2 * buf_ap->GetByteSize()); + } + + return buf_sp; +} + +void +AuxVector::ParseAuxv(DataExtractor &data) +{ + const unsigned int byte_size = m_process->GetAddressByteSize(); + uint32_t offset = 0; + + for (;;) + { + Entry entry; + + if (!ParseAuxvEntry(data, entry, &offset, byte_size)) + break; + + if (entry.type == AT_NULL) + break; + + if (entry.type == AT_IGNORE) + continue; + + m_auxv.push_back(entry); + } +} + +AuxVector::AuxVector(Process *process) + : m_process(process) +{ + DataExtractor data; + LogSP log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + + data.SetData(GetAuxvData()); + data.SetByteOrder(m_process->GetByteOrder()); + data.SetAddressByteSize(m_process->GetAddressByteSize()); + + ParseAuxv(data); + + if (log) + DumpToLog(log); +} + +AuxVector::iterator +AuxVector::FindEntry(EntryType type) const +{ + for (iterator I = begin(); I != end(); ++I) + { + if (I->type == static_cast<uint64_t>(type)) + return I; + } + + return end(); +} + +void +AuxVector::DumpToLog(LogSP log) const +{ + if (!log) + return; + + log->PutCString("AuxVector: "); + for (iterator I = begin(); I != end(); ++I) + { + log->Printf(" %s [%d]: %lx", GetEntryName(*I), I->type, I->value); + } +} + +const char * +AuxVector::GetEntryName(EntryType type) +{ + const char *name; + +#define ENTRY_NAME(_type) _type: name = #_type + switch (type) + { + default: + name = "unkown"; + break; + + case ENTRY_NAME(AT_NULL); break; + case ENTRY_NAME(AT_IGNORE); break; + case ENTRY_NAME(AT_EXECFD); break; + case ENTRY_NAME(AT_PHDR); break; + case ENTRY_NAME(AT_PHENT); break; + case ENTRY_NAME(AT_PHNUM); break; + case ENTRY_NAME(AT_PAGESZ); break; + case ENTRY_NAME(AT_BASE); break; + case ENTRY_NAME(AT_FLAGS); break; + case ENTRY_NAME(AT_ENTRY); break; + case ENTRY_NAME(AT_NOTELF); break; + case ENTRY_NAME(AT_UID); break; + case ENTRY_NAME(AT_EUID); break; + case ENTRY_NAME(AT_GID); break; + case ENTRY_NAME(AT_EGID); break; + case ENTRY_NAME(AT_CLKTCK); break; + } +#undef ENTRY_NAME + + return name; +} + diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.h b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.h new file mode 100644 index 00000000000..7a5b3700fce --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.h @@ -0,0 +1,97 @@ +//===-- AuxVector.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_AuxVector_H_ +#define liblldb_AuxVector_H_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +#include "lldb/lldb-forward-rtti.h" + +namespace lldb_private { +class DataExtractor; +} + +/// @class AuxVector +/// @brief Represents a processes auxiliary vector. +/// +/// When a process is loaded on Linux a vector of values is placed onto the +/// stack communicating operating system specific information. On construction +/// this class locates and parses this information and provides a simple +/// read-only interface to the entries found. +class AuxVector { + +public: + AuxVector(lldb_private::Process *process); + + struct Entry { + uint64_t type; + uint64_t value; + + Entry() : type(0), value(0) { } + }; + + /// Constants describing the type of entry. + enum EntryType { + AT_NULL = 0, ///< End of auxv. + AT_IGNORE = 1, ///< Ignore entry. + AT_EXECFD = 2, ///< File descriptor of program. + AT_PHDR = 3, ///< Program headers. + AT_PHENT = 4, ///< Size of program header. + AT_PHNUM = 5, ///< Number of program headers. + AT_PAGESZ = 6, ///< Page size. + AT_BASE = 7, ///< Interpreter base address. + AT_FLAGS = 8, ///< Flags. + AT_ENTRY = 9, ///< Program entry point. + AT_NOTELF = 10, ///< Set if program is not an ELF. + AT_UID = 11, ///< UID. + AT_EUID = 12, ///< Effective UID. + AT_GID = 13, ///< GID. + AT_EGID = 14, ///< Effective GID. + AT_CLKTCK = 17 ///< Clock frequency (e.g. times(2)). + }; + +private: + typedef std::vector<Entry> EntryVector; + +public: + typedef EntryVector::const_iterator iterator; + + iterator begin() const { return m_auxv.begin(); } + iterator end() const { return m_auxv.end(); } + + iterator + FindEntry(EntryType type) const; + + static const char * + GetEntryName(const Entry &entry) { + return GetEntryName(static_cast<EntryType>(entry.type)); + } + + static const char * + GetEntryName(EntryType type); + + void + DumpToLog(lldb::LogSP log) const; + +private: + lldb_private::Process *m_process; + EntryVector m_auxv; + + lldb::DataBufferSP + GetAuxvData(); + + void + ParseAuxv(lldb_private::DataExtractor &data); +}; + +#endif diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.cpp b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.cpp new file mode 100644 index 00000000000..8b4795d4c49 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.cpp @@ -0,0 +1,315 @@ +//===-- DYLDRendezvous.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 +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "DYLDRendezvous.h" + +using namespace lldb; +using namespace lldb_private; + +/// Locates the address of the rendezvous structure. Returns the address on +/// success and LLDB_INVALID_ADDRESS on failure. +static addr_t +ResolveRendezvousAddress(Process *process) +{ + addr_t info_location; + addr_t info_addr; + Error error; + size_t size; + + info_location = process->GetImageInfoAddress(); + + if (info_location == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + info_addr = 0; + size = process->DoReadMemory(info_location, &info_addr, + process->GetAddressByteSize(), error); + if (size != process->GetAddressByteSize() || error.Fail()) + return LLDB_INVALID_ADDRESS; + + if (info_addr == 0) + return LLDB_INVALID_ADDRESS; + + return info_addr; +} + +DYLDRendezvous::DYLDRendezvous(Process *process) + : m_process(process), + m_rendezvous_addr(LLDB_INVALID_ADDRESS), + m_current(), + m_previous(), + m_soentries(), + m_added_soentries(), + m_removed_soentries() +{ +} + +bool +DYLDRendezvous::Resolve() +{ + const size_t word_size = 4; + Rendezvous info; + size_t address_size; + size_t padding; + addr_t info_addr; + addr_t cursor; + + address_size = m_process->GetAddressByteSize(); + padding = address_size - word_size; + + if (m_rendezvous_addr == LLDB_INVALID_ADDRESS) + cursor = info_addr = ResolveRendezvousAddress(m_process); + else + cursor = info_addr = m_rendezvous_addr; + + if (cursor == LLDB_INVALID_ADDRESS) + return false; + + if (!(cursor = ReadMemory(cursor, &info.version, word_size))) + return false; + + if (!(cursor = ReadMemory(cursor + padding, &info.map_addr, address_size))) + return false; + + if (!(cursor = ReadMemory(cursor, &info.brk, address_size))) + return false; + + if (!(cursor = ReadMemory(cursor, &info.state, word_size))) + return false; + + if (!(cursor = ReadMemory(cursor + padding, &info.ldbase, address_size))) + return false; + + // The rendezvous was successfully read. Update our internal state. + m_rendezvous_addr = info_addr; + m_previous = m_current; + m_current = info; + + return UpdateSOEntries(); +} + +bool +DYLDRendezvous::IsValid() +{ + return m_rendezvous_addr != LLDB_INVALID_ADDRESS; +} + +bool +DYLDRendezvous::UpdateSOEntries() +{ + SOEntry entry; + + if (m_current.map_addr == 0) + return false; + + // If we are about to add or remove a shared object clear out the current + // state and take a snapshot of the currently loaded images. + if (m_current.state == eAdd || m_current.state == eDelete) + { + assert(m_previous.state == eConsistent); + m_soentries.clear(); + m_added_soentries.clear(); + m_removed_soentries.clear(); + return TakeSnapshot(m_soentries); + } + + // Otherwise check the previous state to determine what to expect and update + // accordingly. + if (m_previous.state == eAdd) + return UpdateSOEntriesForAddition(); + else if (m_previous.state == eDelete) + return UpdateSOEntriesForDeletion(); + + return false; +} + +bool +DYLDRendezvous::UpdateSOEntriesForAddition() +{ + SOEntry entry; + iterator pos; + + assert(m_previous.state == eAdd); + + if (m_current.map_addr == 0) + return false; + + for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) + { + if (!ReadSOEntryFromMemory(cursor, entry)) + return false; + + if (entry.path.empty()) + continue; + + pos = std::find(m_soentries.begin(), m_soentries.end(), entry); + if (pos == m_soentries.end()) + { + m_soentries.push_back(entry); + m_added_soentries.push_back(entry); + } + } + + return true; +} + +bool +DYLDRendezvous::UpdateSOEntriesForDeletion() +{ + SOEntryList entry_list; + iterator pos; + + assert(m_previous.state == eDelete); + + if (!TakeSnapshot(entry_list)) + return false; + + for (iterator I = begin(); I != end(); ++I) + { + pos = std::find(entry_list.begin(), entry_list.end(), *I); + if (pos == entry_list.end()) + m_removed_soentries.push_back(*I); + } + + m_soentries = entry_list; + return true; +} + +bool +DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) +{ + SOEntry entry; + + if (m_current.map_addr == 0) + return false; + + for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) + { + if (!ReadSOEntryFromMemory(cursor, entry)) + return false; + + if (entry.path.empty()) + continue; + + entry_list.push_back(entry); + } + + return true; +} + +addr_t +DYLDRendezvous::ReadMemory(addr_t addr, void *dst, size_t size) +{ + size_t bytes_read; + Error error; + + bytes_read = m_process->DoReadMemory(addr, dst, size, error); + if (bytes_read != size || error.Fail()) + return 0; + + return addr + bytes_read; +} + +std::string +DYLDRendezvous::ReadStringFromMemory(addr_t addr) +{ + std::string str; + Error error; + size_t size; + char c; + + if (addr == LLDB_INVALID_ADDRESS) + return std::string(); + + for (;;) { + size = m_process->DoReadMemory(addr, &c, 1, error); + if (size != 1 || error.Fail()) + return std::string(); + if (c == 0) + break; + else { + str.push_back(c); + addr++; + } + } + + return str; +} + +bool +DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) +{ + size_t address_size = m_process->GetAddressByteSize(); + + entry.clear(); + + if (!(addr = ReadMemory(addr, &entry.base_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.path_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.dyn_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.next, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.prev, address_size))) + return false; + + entry.path = ReadStringFromMemory(entry.path_addr); + + return true; +} + +void +DYLDRendezvous::DumpToLog(LogSP log) const +{ + int state = GetState(); + + if (!log) + return; + + log->PutCString("DYLDRendezvous:"); + log->Printf(" Address: %lx", GetRendezvousAddress()); + log->Printf(" Version: %d", GetVersion()); + log->Printf(" Link : %lx", GetLinkMapAddress()); + log->Printf(" Break : %lx", GetBreakAddress()); + log->Printf(" LDBase : %lx", GetLDBase()); + log->Printf(" State : %s", + (state == eConsistent) ? "consistent" : + (state == eAdd) ? "add" : + (state == eDelete) ? "delete" : "unknown"); + + iterator I = begin(); + iterator E = end(); + + if (I != E) + log->PutCString("DYLDRendezvous SOEntries:"); + + for (int i = 1; I != E; ++I, ++i) + { + log->Printf("\n SOEntry [%d] %s", i, I->path.c_str()); + log->Printf(" Base : %lx", I->base_addr); + log->Printf(" Path : %lx", I->path_addr); + log->Printf(" Dyn : %lx", I->dyn_addr); + log->Printf(" Next : %lx", I->next); + log->Printf(" Prev : %lx", I->prev); + } +} diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.h b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.h new file mode 100644 index 00000000000..e8052ae5b3e --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.h @@ -0,0 +1,227 @@ +//===-- DYLDRendezvous.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_Rendezvous_H_ +#define liblldb_Rendezvous_H_ + +// C Includes +// C++ Includes +#include <list> +#include <string> + +// Other libraries and framework includes +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { +class Process; +} + +/// @class DYLDRendezvous +/// @brief Interface to the runtime linker. +/// +/// A structure is present in a processes memory space which is updated by the +/// runtime liker each time a module is loaded or unloaded. This class provides +/// an interface to this structure and maintains a consistent snapshot of the +/// currently loaded modules. +class DYLDRendezvous { + + // This structure is used to hold the contents of the debug rendezvous + // information (struct r_debug) as found in the inferiors memory. Note that + // the layout of this struct is not binary compatible, it is simply large + // enough to hold the information on both 32 and 64 bit platforms. + struct Rendezvous { + uint64_t version; + lldb::addr_t map_addr; + lldb::addr_t brk; + uint64_t state; + lldb::addr_t ldbase; + + Rendezvous() + : version(0), map_addr(0), brk(0), state(0), ldbase(0) { } + }; + +public: + DYLDRendezvous(lldb_private::Process *process); + + /// Update the internal snapshot of runtime linker rendezvous and recompute + /// the currently loaded modules. + /// + /// This method should be called once one start up, then once each time the + /// runtime linker enters the function given by GetBreakAddress(). + /// + /// @returns true on success and false on failure. + /// + /// @see GetBreakAddress(). + bool + Resolve(); + + /// @returns true if this rendezvous has been located in the inferiors + /// address space and false otherwise. + bool + IsValid(); + + /// @returns the address of the rendezvous structure in the inferiors + /// address space. + lldb::addr_t + GetRendezvousAddress() const { return m_rendezvous_addr; } + + /// @returns the version of the rendezvous protocol being used. + int + GetVersion() const { return m_current.version; } + + /// @returns address in the inferiors address space containing the linked + /// list of shared object descriptors. + lldb::addr_t + GetLinkMapAddress() const { return m_current.map_addr; } + + /// A breakpoint should be set at this address and Resolve called on each + /// hit. + /// + /// @returns the address of a function called by the runtime linker each + /// time a module is loaded/unloaded, or about to be loaded/unloaded. + /// + /// @see Resolve() + lldb::addr_t + GetBreakAddress() const { return m_current.brk; } + + /// Returns the current state of the rendezvous structure. + int + GetState() const { return m_current.state; } + + /// @returns the base address of the runtime linker in the inferiors address + /// space. + lldb::addr_t + GetLDBase() const { return m_current.ldbase; } + + /// @returns true if modules have been loaded into the inferior since the + /// last call to Resolve(). + bool + ModulesDidLoad() const { return !m_added_soentries.empty(); } + + /// @returns true if modules have been unloaded from the inferior since the + /// last call to Resolve(). + bool + ModulesDidUnload() const { return !m_removed_soentries.empty(); } + + void + DumpToLog(lldb::LogSP log) const; + + /// @brief Constants describing the state of the rendezvous. + /// + /// @see GetState(). + enum RendezvousState { + eConsistent, + eAdd, + eDelete + }; + + /// @brief Structure representing the shared objects currently loaded into + /// the inferior process. + /// + /// This object is a rough analogue to the struct link_map object which + /// actually lives in the inferiors memory. + struct SOEntry { + lldb::addr_t base_addr; ///< Base address of the loaded object. + lldb::addr_t path_addr; ///< String naming the shared object. + lldb::addr_t dyn_addr; ///< Dynamic section of shared object. + lldb::addr_t next; ///< Address of next so_entry. + lldb::addr_t prev; ///< Address of previous so_entry. + std::string path; ///< File name of shared object. + + SOEntry() { clear(); } + + bool operator ==(const SOEntry &entry) { + return this->path == entry.path; + } + + void clear() { + base_addr = 0; + path_addr = 0; + dyn_addr = 0; + next = 0; + prev = 0; + path.clear(); + } + }; + +protected: + typedef std::list<SOEntry> SOEntryList; + +public: + typedef SOEntryList::const_iterator iterator; + + /// Iterators over all currently loaded modules. + iterator begin() const { return m_soentries.begin(); } + iterator end() const { return m_soentries.end(); } + + /// Iterators over all modules loaded into the inferior since the last call + /// to Resolve(). + iterator loaded_begin() const { return m_added_soentries.begin(); } + iterator loaded_end() const { return m_added_soentries.end(); } + + /// Iterators over all modules unloaded from the inferior since the last + /// call to Resolve(). + iterator unloaded_begin() const { return m_removed_soentries.begin(); } + iterator unloaded_end() const { return m_removed_soentries.end(); } + +protected: + lldb_private::Process *m_process; + + /// Location of the r_debug structure in the inferiors address space. + lldb::addr_t m_rendezvous_addr; + + /// Current and previous snapshots of the rendezvous structure. + Rendezvous m_current; + Rendezvous m_previous; + + /// List of SOEntry objects corresponding to the current link map state. + SOEntryList m_soentries; + + /// List of SOEntry's added to the link map since the last call to Resolve(). + SOEntryList m_added_soentries; + + /// List of SOEntry's removed from the link map since the last call to + /// Resolve(). + SOEntryList m_removed_soentries; + + /// Reads @p size bytes from the inferiors address space starting at @p + /// addr. + /// + /// @returns addr + size if the read was successful and false otherwise. + lldb::addr_t + ReadMemory(lldb::addr_t addr, void *dst, size_t size); + + /// Reads a null-terminated C string from the memory location starting at @p + /// addr. + std::string + ReadStringFromMemory(lldb::addr_t addr); + + /// Reads an SOEntry starting at @p addr. + bool + ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry); + + /// Updates the current set of SOEntries, the set of added entries, and the + /// set of removed entries. + bool + UpdateSOEntries(); + + bool + UpdateSOEntriesForAddition(); + + bool + UpdateSOEntriesForDeletion(); + + /// Reads the current list of shared objects according to the link map + /// supplied by the runtime linker. + bool + TakeSnapshot(SOEntryList &entry_list); +}; + +#endif diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.cpp b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.cpp new file mode 100644 index 00000000000..0802ff65575 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.cpp @@ -0,0 +1,371 @@ +//===-- DynamicLoaderLinux.h ------------------------------------*- 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 +#include <iostream> + +// Other libraries and framework includes +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "AuxVector.h" +#include "DynamicLoaderLinuxDYLD.h" + +using namespace lldb; +using namespace lldb_private; + +void +DynamicLoaderLinuxDYLD::Initialize() +{ + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderLinuxDYLD::Terminate() +{ +} + +const char * +DynamicLoaderLinuxDYLD::GetPluginName() +{ + return "DynamicLoaderLinuxDYLD"; +} + +const char * +DynamicLoaderLinuxDYLD::GetShortPluginName() +{ + return "linux-dyld"; +} + +const char * +DynamicLoaderLinuxDYLD::GetPluginNameStatic() +{ + return "dynamic-loader.linux-dyld"; +} + +const char * +DynamicLoaderLinuxDYLD::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in Linux processes."; +} + +void +DynamicLoaderLinuxDYLD::GetPluginCommandHelp(const char *command, Stream *strm) +{ +} + +uint32_t +DynamicLoaderLinuxDYLD::GetPluginVersion() +{ + return 1; +} + +DynamicLoader * +DynamicLoaderLinuxDYLD::CreateInstance(Process *process) +{ + return new DynamicLoaderLinuxDYLD(process); +} + +DynamicLoaderLinuxDYLD::DynamicLoaderLinuxDYLD(Process *process) + : DynamicLoader(process), + m_rendezvous(process), + m_load_offset(LLDB_INVALID_ADDRESS), + m_entry_point(LLDB_INVALID_ADDRESS), + m_auxv(NULL) +{ +} + +DynamicLoaderLinuxDYLD::~DynamicLoaderLinuxDYLD() +{ +} + +void +DynamicLoaderLinuxDYLD::DidAttach() +{ + ModuleSP executable; + addr_t load_offset; + + m_auxv.reset(new AuxVector(m_process)); + + executable = m_process->GetTarget().GetExecutableModule(); + load_offset = ComputeLoadOffset(); + + if (!executable.empty() && load_offset != LLDB_INVALID_ADDRESS) + { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, load_offset); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +void +DynamicLoaderLinuxDYLD::DidLaunch() +{ + ModuleSP executable; + addr_t load_offset; + + m_auxv.reset(new AuxVector(m_process)); + + executable = m_process->GetTarget().GetExecutableModule(); + load_offset = ComputeLoadOffset(); + + if (!executable.empty() && load_offset != LLDB_INVALID_ADDRESS) + { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, load_offset); + ProbeEntry(); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +Error +DynamicLoaderLinuxDYLD::ExecutePluginCommand(Args &command, Stream *strm) +{ + return Error(); +} + +Log * +DynamicLoaderLinuxDYLD::EnablePluginLogging(Stream *strm, Args &command) +{ + return NULL; +} + +Error +DynamicLoaderLinuxDYLD::CanLoadImage() +{ + return Error(); +} + +void +DynamicLoaderLinuxDYLD::UpdateLoadedSections(ModuleSP module, addr_t base_addr) +{ + ObjectFile *obj_file = module->GetObjectFile(); + SectionList *sections = obj_file->GetSectionList(); + SectionLoadList &load_list = m_process->GetTarget().GetSectionLoadList(); + const size_t num_sections = sections->GetSize(); + + for (unsigned i = 0; i < num_sections; ++i) + { + Section *section = sections->GetSectionAtIndex(i).get(); + lldb::addr_t new_load_addr = section->GetFileAddress() + base_addr; + lldb::addr_t old_load_addr = load_list.GetSectionLoadAddress(section); + + // If the file address of the section is zero then this is not an + // allocatable/loadable section (property of ELF sh_addr). Skip it. + if (new_load_addr == base_addr) + continue; + + if (old_load_addr == LLDB_INVALID_ADDRESS || + old_load_addr != new_load_addr) + load_list.SetSectionLoadAddress(section, new_load_addr); + } +} + +void +DynamicLoaderLinuxDYLD::ProbeEntry() +{ + Breakpoint *entry_break; + addr_t entry; + + if ((entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) + return; + + entry_break = m_process->GetTarget().CreateBreakpoint(entry, true).get(); + entry_break->SetCallback(EntryBreakpointHit, this, true); +} + +// The runtime linker has run and initialized the rendezvous structure once the +// process has hit its entry point. When we hit the corresponding breakpoint we +// interrogate the rendezvous structure to get the load addresses of all +// dependent modules for the process. Similarly, we can discover the runtime +// linker function and setup a breakpoint to notify us of any dynamically loaded +// modules (via dlopen). +bool +DynamicLoaderLinuxDYLD::EntryBreakpointHit(void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + DynamicLoaderLinuxDYLD* dyld_instance; + + dyld_instance = static_cast<DynamicLoaderLinuxDYLD*>(baton); + dyld_instance->LoadAllCurrentModules(); + dyld_instance->SetRendezvousBreakpoint(); + return false; // Continue running. +} + +void +DynamicLoaderLinuxDYLD::SetRendezvousBreakpoint() +{ + Breakpoint *dyld_break; + addr_t break_addr; + + break_addr = m_rendezvous.GetBreakAddress(); + dyld_break = m_process->GetTarget().CreateBreakpoint(break_addr, true).get(); + dyld_break->SetCallback(RendezvousBreakpointHit, this, true); +} + +bool +DynamicLoaderLinuxDYLD::RendezvousBreakpointHit(void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + DynamicLoaderLinuxDYLD* dyld_instance; + + dyld_instance = static_cast<DynamicLoaderLinuxDYLD*>(baton); + dyld_instance->RefreshModules(); + + // Return true to stop the target, false to just let the target run. + return dyld_instance->GetStopWhenImagesChange(); +} + +void +DynamicLoaderLinuxDYLD::RefreshModules() +{ + if (!m_rendezvous.Resolve()) + return; + + DYLDRendezvous::iterator I; + DYLDRendezvous::iterator E; + + ModuleList &loaded_modules = m_process->GetTarget().GetImages(); + + if (m_rendezvous.ModulesDidLoad()) + { + ModuleList new_modules; + + E = m_rendezvous.loaded_end(); + for (I = m_rendezvous.loaded_begin(); I != E; ++I) + { + FileSpec file(I->path.c_str(), true); + ModuleSP module_sp = LoadModuleAtAddress(file, I->base_addr); + if (!module_sp.empty()) + new_modules.Append(module_sp); + } + m_process->GetTarget().ModulesDidLoad(new_modules); + } + + if (m_rendezvous.ModulesDidUnload()) + { + ModuleList old_modules; + + E = m_rendezvous.unloaded_end(); + for (I = m_rendezvous.unloaded_begin(); I != E; ++I) + { + FileSpec file(I->path.c_str(), true); + ModuleSP module_sp = loaded_modules.FindFirstModuleForFileSpec(file); + if (!module_sp.empty()) + old_modules.Append(module_sp); + } + m_process->GetTarget().ModulesDidUnload(old_modules); + } +} + +ThreadPlanSP +DynamicLoaderLinuxDYLD::GetStepThroughTrampolinePlan(Thread &thread, bool stop_others) +{ + LogSP log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + ThreadPlanSP thread_plan_sp; + + if (log) + log->PutCString("DynamicLoaderLinuxDYLD: " + "GetStepThroughTrampolinePlan not implemented\n"); + + return thread_plan_sp; +} + +void +DynamicLoaderLinuxDYLD::LoadAllCurrentModules() +{ + DYLDRendezvous::iterator I; + DYLDRendezvous::iterator E; + ModuleList module_list; + + if (!m_rendezvous.Resolve()) + return; + + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) + { + FileSpec file(I->path.c_str(), false); + ModuleSP module_sp = LoadModuleAtAddress(file, I->base_addr); + if (!module_sp.empty()) + module_list.Append(module_sp); + } + + m_process->GetTarget().ModulesDidLoad(module_list); +} + +ModuleSP +DynamicLoaderLinuxDYLD::LoadModuleAtAddress(const FileSpec &file, addr_t base_addr) +{ + Target &target = m_process->GetTarget(); + ModuleList &modules = target.GetImages(); + ModuleSP module_sp; + + if ((module_sp = modules.FindFirstModuleForFileSpec(file))) + { + UpdateLoadedSections(module_sp, base_addr); + } + else if ((module_sp = target.GetSharedModule(file, target.GetArchitecture()))) + { + UpdateLoadedSections(module_sp, base_addr); + modules.Append(module_sp); + } + + return module_sp; +} + +addr_t +DynamicLoaderLinuxDYLD::ComputeLoadOffset() +{ + addr_t virt_entry; + + if (m_load_offset != LLDB_INVALID_ADDRESS) + return m_load_offset; + + if ((virt_entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + ModuleSP module = m_process->GetTarget().GetExecutableModule(); + ObjectFile *exe = module->GetObjectFile(); + Address file_entry = exe->GetEntryPoint(); + + if (!file_entry.IsValid()) + return LLDB_INVALID_ADDRESS; + + m_load_offset = virt_entry - file_entry.GetFileAddress(); + return m_load_offset; +} + +addr_t +DynamicLoaderLinuxDYLD::GetEntryPoint() +{ + if (m_entry_point != LLDB_INVALID_ADDRESS) + return m_entry_point; + + if (m_auxv.get() == NULL) + return LLDB_INVALID_ADDRESS; + + AuxVector::iterator I = m_auxv->FindEntry(AuxVector::AT_ENTRY); + + if (I == m_auxv->end()) + return LLDB_INVALID_ADDRESS; + + m_entry_point = static_cast<addr_t>(I->value); + return m_entry_point; +} diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.h b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.h new file mode 100644 index 00000000000..bc537ce7746 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.h @@ -0,0 +1,165 @@ +//===-- DynamicLoaderLinux.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_DynamicLoaderLinux_H_ +#define liblldb_DynamicLoaderLinux_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/DynamicLoader.h" + +#include "DYLDRendezvous.h" + +class AuxVector; + +class DynamicLoaderLinuxDYLD : public lldb_private::DynamicLoader +{ +public: + + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance(lldb_private::Process *process); + + DynamicLoaderLinuxDYLD(lldb_private::Process *process); + + virtual + ~DynamicLoaderLinuxDYLD(); + + //------------------------------------------------------------------ + // DynamicLoader protocol + //------------------------------------------------------------------ + + virtual void + DidAttach(); + + virtual void + DidLaunch(); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others); + + virtual lldb_private::Error + CanLoadImage(); + + //------------------------------------------------------------------ + // 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: + /// Runtime linker rendezvous structure. + DYLDRendezvous m_rendezvous; + + /// Virtual load address of the inferior process. + lldb::addr_t m_load_offset; + + /// Virtual entry address of the inferior process. + lldb::addr_t m_entry_point; + + /// Auxiliary vector of the inferior process. + std::auto_ptr<AuxVector> m_auxv; + + /// Enables a breakpoint on a function called by the runtime + /// linker each time a module is loaded or unloaded. + void + SetRendezvousBreakpoint(); + + /// Callback routine which updates the current list of loaded modules based + /// on the information supplied by the runtime linker. + static bool + RendezvousBreakpointHit(void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + /// Helper method for RendezvousBreakpointHit. Updates LLDB's current set + /// of loaded modules. + void + RefreshModules(); + + /// Updates the load address of every allocatable section in @p module. + /// + /// @param module The module to traverse. + /// + /// @param base_addr The virtual base address @p module is loaded at. + void + UpdateLoadedSections(lldb::ModuleSP module, + lldb::addr_t base_addr = 0); + + /// Locates or creates a module given by @p file and updates/loads the + /// resulting module at the virtual base address @p base_addr. + lldb::ModuleSP + LoadModuleAtAddress(const lldb_private::FileSpec &file, lldb::addr_t base_addr); + + /// Resolves the entry point for the current inferior process and sets a + /// breakpoint at that address. + void + ProbeEntry(); + + /// Callback routine invoked when we hit the breakpoint on process entry. + /// + /// This routine is responsible for resolving the load addresses of all + /// dependent modules required by the inferior and setting up the rendezvous + /// breakpoint. + static bool + EntryBreakpointHit(void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + /// Helper for the entry breakpoint callback. Resolves the load addresses + /// of all dependent modules. + void + LoadAllCurrentModules(); + + /// Computes a value for m_load_offset returning the computed address on + /// success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t + ComputeLoadOffset(); + + /// Computes a value for m_entry_point returning the computed address on + /// success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t + GetEntryPoint(); + +private: + DISALLOW_COPY_AND_ASSIGN(DynamicLoaderLinuxDYLD); +}; + +#endif // liblldb_DynamicLoaderLinuxDYLD_H_ diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/Makefile b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/Makefile new file mode 100644 index 00000000000..94025dd3eba --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/Linux-DYLD/Makefile ----*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginDynamicLoaderLinux +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/lldb/source/Plugins/Makefile b/lldb/source/Plugins/Makefile index 14eed8ad8b1..a3d644f78fb 100644 --- a/lldb/source/Plugins/Makefile +++ b/lldb/source/Plugins/Makefile @@ -23,7 +23,7 @@ DIRS += DynamicLoader/MacOSX-DYLD ObjectContainer/Universal-Mach-O \ endif ifeq ($(HOST_OS),Linux) -DIRS += Process/Linux +DIRS += Process/Linux DynamicLoader/Linux-DYLD endif include $(LLDB_LEVEL)/Makefile diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp index 00bdda51036..d7fe1dae0a1 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp @@ -13,6 +13,7 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Target.h" #include "ProcessLinux.h" @@ -102,6 +103,19 @@ ProcessLinux::DoAttachToProcessWithID(lldb::pid_t pid) } Error +ProcessLinux::WillLaunch(Module* module) +{ + Error error; + + m_dyld_ap.reset(DynamicLoader::FindPlugin(this, "dynamic-loader.linux-dyld")); + if (m_dyld_ap.get() == NULL) + error.SetErrorString("unable to find the dynamic loader named " + "'dynamic-loader.linux-dyld'"); + + return error; +} + +Error ProcessLinux::DoLaunch(Module *module, char const *argv[], char const *envp[], @@ -128,6 +142,13 @@ ProcessLinux::DoLaunch(Module *module, return error; } +void +ProcessLinux::DidLaunch() +{ + if (m_dyld_ap.get() != NULL) + m_dyld_ap->DidLaunch(); +} + Error ProcessLinux::DoResume() { @@ -371,6 +392,12 @@ ProcessLinux::GetByteOrder() const return m_byte_order; } +DynamicLoader * +ProcessLinux::GetDynamicLoader() +{ + return m_dyld_ap.get(); +} + //------------------------------------------------------------------------------ // ProcessInterface protocol. diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinux.h b/lldb/source/Plugins/Process/Linux/ProcessLinux.h index 3e23a7ad37e..fbf14df1988 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/ProcessLinux.h @@ -60,6 +60,9 @@ public: CanDebug(lldb_private::Target &target); virtual lldb_private::Error + WillLaunch(lldb_private::Module *module); + + virtual lldb_private::Error DoAttachToProcessWithID(lldb::pid_t pid); virtual lldb_private::Error @@ -71,6 +74,9 @@ public: const char *stdout_path, const char *stderr_path); + virtual void + DidLaunch(); + virtual lldb_private::Error DoResume(); @@ -131,6 +137,9 @@ public: virtual lldb::addr_t GetImageInfoAddress(); + virtual lldb_private::DynamicLoader * + GetDynamicLoader(); + //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ @@ -176,6 +185,9 @@ private: lldb_private::Mutex m_message_mutex; std::queue<ProcessMessage> m_message_queue; + /// Dynamic loader plugin associated with this process. + std::auto_ptr<lldb_private::DynamicLoader> m_dyld_ap; + /// Updates the loaded sections provided by the executable. /// /// FIXME: It would probably be better to delegate this task to the diff --git a/lldb/source/lldb.cpp b/lldb/source/lldb.cpp index 05c7f4b4517..ac281a269bc 100644 --- a/lldb/source/lldb.cpp +++ b/lldb/source/lldb.cpp @@ -44,6 +44,7 @@ #ifdef __linux__ #include "Plugins/Process/Linux/ProcessLinux.h" +#include "Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.h" #endif using namespace lldb; @@ -94,6 +95,7 @@ lldb_private::Initialize () #endif #ifdef __linux__ ProcessLinux::Initialize(); + DynamicLoaderLinuxDYLD::Initialize(); #endif } } @@ -137,6 +139,7 @@ lldb_private::Terminate () #ifdef __linux__ ProcessLinux::Terminate(); + DynamicLoaderLinuxDYLD::Terminate(); #endif Log::Terminate(); |