diff options
author | Johnny Chen <johnny.chen@apple.com> | 2012-01-05 21:48:15 +0000 |
---|---|---|
committer | Johnny Chen <johnny.chen@apple.com> | 2012-01-05 21:48:15 +0000 |
commit | 9ed5b49c45ba377cffbc5d0d893b5437f50228af (patch) | |
tree | 386909c95bde01a8a2071c1b5f7d2c78b5187c5a /lldb/source/Plugins | |
parent | 9afd449e8ff4382d9479cfbc7acc38d74fbcfc76 (diff) | |
download | bcm5719-llvm-9ed5b49c45ba377cffbc5d0d893b5437f50228af.tar.gz bcm5719-llvm-9ed5b49c45ba377cffbc5d0d893b5437f50228af.zip |
Fix incomplete commit of http://llvm.org/viewvc/llvm-project?rev=147609&view=rev:
This patch combines common code from Linux and FreeBSD into
a new POSIX platform. It also contains fixes for 64bit FreeBSD.
The patch is based on changes by Mark Peek <mp@FreeBSD.org> and
"K. Macy" <kmacy@freebsd.org> in their github repo located at
https://github.com/fbsd/lldb.
llvm-svn: 147613
Diffstat (limited to 'lldb/source/Plugins')
48 files changed, 7397 insertions, 16 deletions
diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.cpp b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.cpp deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.cpp +++ /dev/null diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.h b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.h deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/AuxVector.h +++ /dev/null diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.cpp b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.cpp deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.cpp +++ /dev/null diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.h b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.h deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DYLDRendezvous.h +++ /dev/null diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.cpp b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.cpp deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.cpp +++ /dev/null diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.h b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.h deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/DynamicLoaderLinuxDYLD.h +++ /dev/null diff --git a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/Makefile b/lldb/source/Plugins/DynamicLoader/Linux-DYLD/Makefile deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/DynamicLoader/Linux-DYLD/Makefile +++ /dev/null diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp new file mode 100644 index 00000000000..9311e70ca33 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp @@ -0,0 +1,153 @@ +//===-- 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() +{ + + return lldb_private::Host::GetAuxvData(m_process); +} + +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/POSIX-DYLD/AuxVector.h b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h new file mode 100644 index 00000000000..7a5b3700fce --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/POSIX-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/POSIX-DYLD/DYLDRendezvous.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp new file mode 100644 index 00000000000..aa00f54560f --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp @@ -0,0 +1,330 @@ +//===-- 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() +{ + // Cache a copy of the executable path + m_process->GetTarget().GetExecutableModule().get()->GetFileSpec().GetPath(m_exe_path, PATH_MAX); +} + +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; + + // When the previous and current states are consistent this is the first + // time we have been asked to update. Just take a snapshot of the currently + // loaded modules. + if (m_previous.state == eConsistent && m_current.state == eConsistent) + return TakeSnapshot(m_soentries); + + // 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); + } + assert(m_current.state == eConsistent); + + // 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; + + // Only add shared libraries and not the executable. + // On Linux this is indicated by an empty path in the entry. + // On FreeBSD it is the name of the executable. + if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0) + 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; + + // Only add shared libraries and not the executable. + // On Linux this is indicated by an empty path in the entry. + // On FreeBSD it is the name of the executable. + if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0) + 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/POSIX-DYLD/DYLDRendezvous.h b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h new file mode 100644 index 00000000000..3402a72ce91 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h @@ -0,0 +1,230 @@ +//===-- 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; + + // Cached copy of executable pathname + char m_exe_path[PATH_MAX]; + + /// 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/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp new file mode 100644 index 00000000000..05100cbdb24 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -0,0 +1,425 @@ +//===-- DynamicLoaderPOSIX.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 +// 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 "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" + +#include "AuxVector.h" +#include "DynamicLoaderPOSIXDYLD.h" + +using namespace lldb; +using namespace lldb_private; + +void +DynamicLoaderPOSIXDYLD::Initialize() +{ + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderPOSIXDYLD::Terminate() +{ +} + +const char * +DynamicLoaderPOSIXDYLD::GetPluginName() +{ + return "DynamicLoaderPOSIXDYLD"; +} + +const char * +DynamicLoaderPOSIXDYLD::GetShortPluginName() +{ + return "linux-dyld"; +} + +const char * +DynamicLoaderPOSIXDYLD::GetPluginNameStatic() +{ + return "dynamic-loader.linux-dyld"; +} + +const char * +DynamicLoaderPOSIXDYLD::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in POSIX processes."; +} + +void +DynamicLoaderPOSIXDYLD::GetPluginCommandHelp(const char *command, Stream *strm) +{ +} + +uint32_t +DynamicLoaderPOSIXDYLD::GetPluginVersion() +{ + return 1; +} + +DynamicLoader * +DynamicLoaderPOSIXDYLD::CreateInstance(Process *process, bool force) +{ + bool create = force; + if (!create) + { + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getOS() == llvm::Triple::Linux || + triple_ref.getOS() == llvm::Triple::FreeBSD) + create = true; + } + + if (create) + return new DynamicLoaderPOSIXDYLD (process); + return NULL; +} + +DynamicLoaderPOSIXDYLD::DynamicLoaderPOSIXDYLD(Process *process) + : DynamicLoader(process), + m_rendezvous(process), + m_load_offset(LLDB_INVALID_ADDRESS), + m_entry_point(LLDB_INVALID_ADDRESS), + m_auxv(NULL) +{ +} + +DynamicLoaderPOSIXDYLD::~DynamicLoaderPOSIXDYLD() +{ +} + +void +DynamicLoaderPOSIXDYLD::DidAttach() +{ + ModuleSP executable; + addr_t load_offset; + + m_auxv.reset(new AuxVector(m_process)); + + executable = m_process->GetTarget().GetExecutableModule(); + load_offset = ComputeLoadOffset(); + + if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) + { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, load_offset); + LoadAllCurrentModules(); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +void +DynamicLoaderPOSIXDYLD::DidLaunch() +{ + ModuleSP executable; + addr_t load_offset; + + m_auxv.reset(new AuxVector(m_process)); + + executable = m_process->GetTarget().GetExecutableModule(); + load_offset = ComputeLoadOffset(); + + if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) + { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, load_offset); + ProbeEntry(); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +Error +DynamicLoaderPOSIXDYLD::ExecutePluginCommand(Args &command, Stream *strm) +{ + return Error(); +} + +Log * +DynamicLoaderPOSIXDYLD::EnablePluginLogging(Stream *strm, Args &command) +{ + return NULL; +} + +Error +DynamicLoaderPOSIXDYLD::CanLoadImage() +{ + return Error(); +} + +void +DynamicLoaderPOSIXDYLD::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 +DynamicLoaderPOSIXDYLD::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 +DynamicLoaderPOSIXDYLD::EntryBreakpointHit(void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + DynamicLoaderPOSIXDYLD* dyld_instance; + + dyld_instance = static_cast<DynamicLoaderPOSIXDYLD*>(baton); + dyld_instance->LoadAllCurrentModules(); + dyld_instance->SetRendezvousBreakpoint(); + return false; // Continue running. +} + +void +DynamicLoaderPOSIXDYLD::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 +DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + DynamicLoaderPOSIXDYLD* dyld_instance; + + dyld_instance = static_cast<DynamicLoaderPOSIXDYLD*>(baton); + dyld_instance->RefreshModules(); + + // Return true to stop the target, false to just let the target run. + return dyld_instance->GetStopWhenImagesChange(); +} + +void +DynamicLoaderPOSIXDYLD::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.get()) + 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, NULL, NULL); + if (module_sp.get()) + old_modules.Append(module_sp); + } + m_process->GetTarget().ModulesDidUnload(old_modules); + } +} + +ThreadPlanSP +DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, bool stop) +{ + LogSP log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + ThreadPlanSP thread_plan_sp; + + StackFrame *frame = thread.GetStackFrameAtIndex(0).get(); + const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol); + Symbol *sym = context.symbol; + + if (sym == NULL || !sym->IsTrampoline()) + return thread_plan_sp; + + const ConstString &sym_name = sym->GetMangled().GetName(Mangled::ePreferMangled); + if (!sym_name) + return thread_plan_sp; + + SymbolContextList target_symbols; + Target &target = thread.GetProcess().GetTarget(); + ModuleList &images = target.GetImages(); + + images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols); + size_t num_targets = target_symbols.GetSize(); + if (!num_targets) + return thread_plan_sp; + + typedef std::vector<lldb::addr_t> AddressVector; + AddressVector addrs; + for (size_t i = 0; i < num_targets; ++i) + { + SymbolContext context; + AddressRange range; + if (target_symbols.GetContextAtIndex(i, context)) + { + context.GetAddressRange(eSymbolContextEverything, 0, false, range); + lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target); + if (addr != LLDB_INVALID_ADDRESS) + addrs.push_back(addr); + } + } + + if (addrs.size() > 0) + { + AddressVector::iterator start = addrs.begin(); + AddressVector::iterator end = addrs.end(); + + std::sort(start, end); + addrs.erase(std::unique(start, end), end); + thread_plan_sp.reset(new ThreadPlanRunToAddress(thread, addrs, stop)); + } + + return thread_plan_sp; +} + +void +DynamicLoaderPOSIXDYLD::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.get()) + module_list.Append(module_sp); + } + + m_process->GetTarget().ModulesDidLoad(module_list); +} + +ModuleSP +DynamicLoaderPOSIXDYLD::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, NULL, NULL))) + { + 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 +DynamicLoaderPOSIXDYLD::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->GetEntryPointAddress(); + + if (!file_entry.IsValid()) + return LLDB_INVALID_ADDRESS; + + m_load_offset = virt_entry - file_entry.GetFileAddress(); + return m_load_offset; +} + +addr_t +DynamicLoaderPOSIXDYLD::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/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h new file mode 100644 index 00000000000..8cca6ee3854 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -0,0 +1,165 @@ +//===-- DynamicLoaderPOSIX.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_DynamicLoaderPOSIX_H_ +#define liblldb_DynamicLoaderPOSIX_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 DynamicLoaderPOSIXDYLD : 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, bool force); + + DynamicLoaderPOSIXDYLD(lldb_private::Process *process); + + virtual + ~DynamicLoaderPOSIXDYLD(); + + //------------------------------------------------------------------ + // 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(DynamicLoaderPOSIXDYLD); +}; + +#endif // liblldb_DynamicLoaderPOSIXDYLD_H_ diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/Makefile b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/Makefile new file mode 100644 index 00000000000..70ea1f34e2b --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/POSIX-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 := lldbPluginDynamicLoaderPOSIX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/lldb/source/Plugins/Process/FreeBSD/Makefile b/lldb/source/Plugins/Process/FreeBSD/Makefile new file mode 100644 index 00000000000..8d16a4a2b4c --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSD/Makefile @@ -0,0 +1,17 @@ +##===- source/Plugins/Process/FreeBSD/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 := lldbPluginProcessFreeBSD +BUILD_ARCHIVE = 1 + +# Extend the include path so we may locate UnwindLLDB.h +CPPFLAGS += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility + +include $(LLDB_LEVEL)/Makefile diff --git a/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp new file mode 100644 index 00000000000..8a77ce50840 --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp @@ -0,0 +1,136 @@ +//===-- ProcessFreeBSD.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 <errno.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Target.h" + +#include "ProcessFreeBSD.h" +#include "ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/InferiorCallPOSIX.h" +#include "ProcessMonitor.h" +#include "POSIXThread.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------------ +// Static functions. + +Process* +ProcessFreeBSD::CreateInstance(Target& target, Listener &listener) +{ + return new ProcessFreeBSD(target, listener); +} + +void +ProcessFreeBSD::Initialize() +{ + static bool g_initialized = false; + + if (!g_initialized) + { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + Log::Callbacks log_callbacks = { + ProcessPOSIXLog::DisableLog, + ProcessPOSIXLog::EnableLog, + ProcessPOSIXLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessFreeBSD::GetPluginNameStatic(), log_callbacks); + ProcessPOSIXLog::RegisterPluginName(GetPluginNameStatic()); + g_initialized = true; + } +} + +const char * +ProcessFreeBSD::GetPluginNameStatic() +{ + return "freebsd"; +} + +const char * +ProcessFreeBSD::GetPluginDescriptionStatic() +{ + return "Process plugin for FreeBSD"; +} + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + +const char * +ProcessFreeBSD::GetPluginName() +{ + return "process.freebsd"; +} + +const char * +ProcessFreeBSD::GetShortPluginName() +{ + return "process.freebsd"; +} + +uint32_t +ProcessFreeBSD::GetPluginVersion() +{ + return 1; +} + +void +ProcessFreeBSD::GetPluginCommandHelp(const char *command, Stream *strm) +{ +} + +Error +ProcessFreeBSD::ExecutePluginCommand(Args &command, Stream *strm) +{ + return Error(1, eErrorTypeGeneric); +} + +Log * +ProcessFreeBSD::EnablePluginLogging(Stream *strm, Args &command) +{ + return NULL; +} + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessFreeBSD::ProcessFreeBSD(Target& target, Listener &listener) + : ProcessPOSIX(target, listener) +{ + // FIXME: Putting this code in the ctor and saving the byte order in a + // member variable is a hack to avoid const qual issues in GetByteOrder. + ObjectFile *obj_file = GetTarget().GetExecutableModule()->GetObjectFile(); + m_byte_order = obj_file->GetByteOrder(); +} + +void +ProcessFreeBSD::Terminate() +{ +} + +uint32_t +ProcessFreeBSD::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + // XXX haxx + new_thread_list = old_thread_list; + + return 0; +} diff --git a/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h b/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h new file mode 100644 index 00000000000..9a511b91c2f --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h @@ -0,0 +1,84 @@ +//===-- ProcessFreeBSD.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_ProcessFreeBSD_H_ +#define liblldb_ProcessFreeBSD_H_ + +// C Includes + +// C++ Includes +#include <queue> + +// Other libraries and framework includes +#include "lldb/Target/Process.h" +#include "lldb/Target/ThreadList.h" +#include "ProcessMessage.h" +#include "ProcessPOSIX.h" + +class ProcessMonitor; + +class ProcessFreeBSD : + public ProcessPOSIX +{ + +public: + //------------------------------------------------------------------ + // Static functions. + //------------------------------------------------------------------ + static Process* + CreateInstance(lldb_private::Target& target, + lldb_private::Listener &listener); + + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessFreeBSD(lldb_private::Target& target, + lldb_private::Listener &listener); + + virtual uint32_t + UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list); + + //------------------------------------------------------------------ + // 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); + +}; + +#endif // liblldb_MacOSXProcess_H_ diff --git a/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp new file mode 100644 index 00000000000..796265ac6fd --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp @@ -0,0 +1,1558 @@ +//===-- ProcessMonitor.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 <errno.h> +#include <poll.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/PseudoTerminal.h" + + +#include "POSIXThread.h" +#include "ProcessFreeBSD.h" +#include "ProcessPOSIXLog.h" +#include "ProcessMonitor.h" + +extern "C" { + extern char ** environ; + } + +using namespace lldb; +using namespace lldb_private; + +// We disable the tracing of ptrace calls for integration builds to +// avoid the additional indirection and checks. +#ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION +// Wrapper for ptrace to catch errors and log calls. + +const char * +Get_PT_IO_OP(int op) +{ + switch (op) { + case PIOD_READ_D: return "READ_D"; + case PIOD_WRITE_D: return "WRITE_D"; + case PIOD_READ_I: return "READ_I"; + case PIOD_WRITE_I: return "WRITE_I"; + default: return "Unknown op"; + } +} + +extern long +PtraceWrapper(int req, ::pid_t pid, void *addr, int data, + const char* reqName, const char* file, int line) +{ + long int result; + + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); + + if (log) { + log->Printf("ptrace(%s, %u, %p, %x) called from file %s line %d", + reqName, pid, addr, data, file, line); + if (req == PT_IO) { + struct ptrace_io_desc *pi = (struct ptrace_io_desc *) addr; + + log->Printf("PT_IO: op=%s offs=%zx size=%ld", + Get_PT_IO_OP(pi->piod_op), pi->piod_offs, pi->piod_len); + } + } + + //PtraceDisplayBytes(req, data); + + errno = 0; + result = ptrace(req, pid, (caddr_t) addr, data); + + //PtraceDisplayBytes(req, data); + + if (log && (result == -1 || errno != 0)) + { + const char* str; + switch (errno) + { + case ESRCH: str = "ESRCH"; break; + case EINVAL: str = "EINVAL"; break; + case EBUSY: str = "EBUSY"; break; + case EPERM: str = "EPERM"; break; + default: str = "<unknown>"; + } + log->Printf("ptrace() failed; errno=%d (%s)", errno, str); + } + + if (log) { + if (req == PT_GETREGS) { + struct reg *r = (struct reg *) addr; + + log->Printf("PT_GETREGS: ip=0x%lx", r->r_rip); + log->Printf("PT_GETREGS: sp=0x%lx", r->r_rsp); + log->Printf("PT_GETREGS: bp=0x%lx", r->r_rbp); + log->Printf("PT_GETREGS: ax=0x%lx", r->r_rax); + } + } + + return result; +} + +#define PTRACE(req, pid, addr, data) \ + PtraceWrapper((req), (pid), (addr), (data), #req, __FILE__, __LINE__) +#else +#define PTRACE ptrace +#endif + +//------------------------------------------------------------------------------ +// Static implementations of ProcessMonitor::ReadMemory and +// ProcessMonitor::WriteMemory. This enables mutual recursion between these +// functions without needed to go thru the thread funnel. + +static size_t +DoReadMemory(lldb::pid_t pid, lldb::addr_t vm_addr, void *buf, size_t size, + Error &error) +{ + struct ptrace_io_desc pi_desc; + + pi_desc.piod_op = PIOD_READ_D; + pi_desc.piod_offs = (void *)vm_addr; + pi_desc.piod_addr = buf; + pi_desc.piod_len = size; + + if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) + error.SetErrorToErrno(); + return pi_desc.piod_len; +} + +static size_t +DoWriteMemory(lldb::pid_t pid, lldb::addr_t vm_addr, const void *buf, + size_t size, Error &error) +{ + struct ptrace_io_desc pi_desc; + + pi_desc.piod_op = PIOD_WRITE_D; + pi_desc.piod_offs = (void *)vm_addr; + pi_desc.piod_addr = (void *)buf; + pi_desc.piod_len = size; + + if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) + error.SetErrorToErrno(); + return pi_desc.piod_len; +} + +// Simple helper function to ensure flags are enabled on the given file +// descriptor. +static bool +EnsureFDFlags(int fd, int flags, Error &error) +{ + int status; + + if ((status = fcntl(fd, F_GETFL)) == -1) + { + error.SetErrorToErrno(); + return false; + } + + if (fcntl(fd, F_SETFL, status | flags) == -1) + { + error.SetErrorToErrno(); + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ +/// @class Operation +/// @brief Represents a ProcessMonitor operation. +/// +/// Under FreeBSD, it is not possible to ptrace() from any other thread but the +/// one that spawned or attached to the process from the start. Therefore, when +/// a ProcessMonitor is asked to deliver or change the state of an inferior +/// process the operation must be "funneled" to a specific thread to perform the +/// task. The Operation class provides an abstract base for all services the +/// ProcessMonitor must perform via the single virtual function Execute, thus +/// encapsulating the code that needs to run in the privileged context. +class Operation +{ +public: + virtual void Execute(ProcessMonitor *monitor) = 0; +}; + +//------------------------------------------------------------------------------ +/// @class ReadOperation +/// @brief Implements ProcessMonitor::ReadMemory. +class ReadOperation : public Operation +{ +public: + ReadOperation(lldb::addr_t addr, void *buff, size_t size, + Error &error, size_t &result) + : m_addr(addr), m_buff(buff), m_size(size), + m_error(error), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::addr_t m_addr; + void *m_buff; + size_t m_size; + Error &m_error; + size_t &m_result; +}; + +void +ReadOperation::Execute(ProcessMonitor *monitor) +{ + lldb::pid_t pid = monitor->GetPID(); + + m_result = DoReadMemory(pid, m_addr, m_buff, m_size, m_error); +} + +//------------------------------------------------------------------------------ +/// @class ReadOperation +/// @brief Implements ProcessMonitor::WriteMemory. +class WriteOperation : public Operation +{ +public: + WriteOperation(lldb::addr_t addr, const void *buff, size_t size, + Error &error, size_t &result) + : m_addr(addr), m_buff(buff), m_size(size), + m_error(error), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::addr_t m_addr; + const void *m_buff; + size_t m_size; + Error &m_error; + size_t &m_result; +}; + +void +WriteOperation::Execute(ProcessMonitor *monitor) +{ + lldb::pid_t pid = monitor->GetPID(); + + m_result = DoWriteMemory(pid, m_addr, m_buff, m_size, m_error); +} + +//------------------------------------------------------------------------------ +/// @class ReadRegOperation +/// @brief Implements ProcessMonitor::ReadRegisterValue. +class ReadRegOperation : public Operation +{ +public: + ReadRegOperation(unsigned offset, unsigned size, RegisterValue &value, bool &result) + : m_offset(offset), m_size(size), m_value(value), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + unsigned m_offset; + unsigned m_size; + RegisterValue &m_value; + bool &m_result; +}; + +void +ReadRegOperation::Execute(ProcessMonitor *monitor) +{ + lldb::pid_t pid = monitor->GetPID(); + struct reg regs; + int rc; + + if ((rc = PTRACE(PT_GETREGS, pid, (caddr_t)®s, 0)) < 0) { + m_result = false; + } else { + if (m_size == sizeof(uintptr_t)) + m_value = *(uintptr_t *)(((caddr_t)®s) + m_offset); + else + memcpy(&m_value, (((caddr_t)®s) + m_offset), m_size); + m_result = true; + } +} + +//------------------------------------------------------------------------------ +/// @class WriteRegOperation +/// @brief Implements ProcessMonitor::WriteRegisterValue. +class WriteRegOperation : public Operation +{ +public: + WriteRegOperation(unsigned offset, const RegisterValue &value, bool &result) + : m_offset(offset), m_value(value), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + unsigned m_offset; + const RegisterValue &m_value; + bool &m_result; +}; + +void +WriteRegOperation::Execute(ProcessMonitor *monitor) +{ + lldb::pid_t pid = monitor->GetPID(); + struct reg regs; + + if (PTRACE(PT_GETREGS, pid, (caddr_t)®s, 0) < 0) { + m_result = false; + return; + } + *(uintptr_t *)(((caddr_t)®s) + m_offset) = (uintptr_t)m_value.GetAsUInt64(); + if (PTRACE(PT_SETREGS, pid, (caddr_t)®s, 0) < 0) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class ReadGPROperation +/// @brief Implements ProcessMonitor::ReadGPR. +class ReadGPROperation : public Operation +{ +public: + ReadGPROperation(void *buf, bool &result) + : m_buf(buf), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + void *m_buf; + bool &m_result; +}; + +void +ReadGPROperation::Execute(ProcessMonitor *monitor) +{ + int rc; + + errno = 0; + rc = PTRACE(PT_GETREGS, monitor->GetPID(), (caddr_t)m_buf, 0); + if (errno != 0) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class ReadFPROperation +/// @brief Implements ProcessMonitor::ReadFPR. +class ReadFPROperation : public Operation +{ +public: + ReadFPROperation(void *buf, bool &result) + : m_buf(buf), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + void *m_buf; + bool &m_result; +}; + +void +ReadFPROperation::Execute(ProcessMonitor *monitor) +{ + if (PTRACE(PT_GETFPREGS, monitor->GetPID(), (caddr_t)m_buf, 0) < 0) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class WriteGPROperation +/// @brief Implements ProcessMonitor::WriteGPR. +class WriteGPROperation : public Operation +{ +public: + WriteGPROperation(void *buf, bool &result) + : m_buf(buf), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + void *m_buf; + bool &m_result; +}; + +void +WriteGPROperation::Execute(ProcessMonitor *monitor) +{ + if (PTRACE(PT_SETREGS, monitor->GetPID(), (caddr_t)m_buf, 0) < 0) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class WriteFPROperation +/// @brief Implements ProcessMonitor::WriteFPR. +class WriteFPROperation : public Operation +{ +public: + WriteFPROperation(void *buf, bool &result) + : m_buf(buf), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + void *m_buf; + bool &m_result; +}; + +void +WriteFPROperation::Execute(ProcessMonitor *monitor) +{ + if (PTRACE(PT_SETFPREGS, monitor->GetPID(), (caddr_t)m_buf, 0) < 0) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class ResumeOperation +/// @brief Implements ProcessMonitor::Resume. +class ResumeOperation : public Operation +{ +public: + ResumeOperation(lldb::tid_t tid, uint32_t signo, bool &result) : + m_tid(tid), m_signo(signo), m_result(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + uint32_t m_signo; + bool &m_result; +}; + +void +ResumeOperation::Execute(ProcessMonitor *monitor) +{ + int data = 0; + + if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) + data = m_signo; + + if (PTRACE(PT_CONTINUE, m_tid, (caddr_t)1, data)) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class ResumeOperation +/// @brief Implements ProcessMonitor::SingleStep. +class SingleStepOperation : public Operation +{ +public: + SingleStepOperation(lldb::tid_t tid, uint32_t signo, bool &result) + : m_tid(tid), m_signo(signo), m_result(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + uint32_t m_signo; + bool &m_result; +}; + +void +SingleStepOperation::Execute(ProcessMonitor *monitor) +{ + int data = 0; + + if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) + data = m_signo; + + if (PTRACE(PT_STEP, m_tid, NULL, data)) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class SiginfoOperation +/// @brief Implements ProcessMonitor::GetSignalInfo. +class SiginfoOperation : public Operation +{ +public: + SiginfoOperation(lldb::tid_t tid, void *info, bool &result) + : m_tid(tid), m_info(info), m_result(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + void *m_info; + bool &m_result; +}; + +void +SiginfoOperation::Execute(ProcessMonitor *monitor) +{ + struct ptrace_lwpinfo plwp; + + if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) + m_result = false; + else { + memcpy(m_info, &plwp.pl_siginfo, sizeof(siginfo_t)); + m_result = true; + } +} + +//------------------------------------------------------------------------------ +/// @class EventMessageOperation +/// @brief Implements ProcessMonitor::GetEventMessage. +class EventMessageOperation : public Operation +{ +public: + EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result) + : m_tid(tid), m_message(message), m_result(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + unsigned long *m_message; + bool &m_result; +}; + +void +EventMessageOperation::Execute(ProcessMonitor *monitor) +{ + struct ptrace_lwpinfo plwp; + + if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) + m_result = false; + else { + if (plwp.pl_flags & PL_FLAG_FORKED) { + m_message = (unsigned long *)plwp.pl_child_pid; + m_result = true; + } else + m_result = false; + } +} + +//------------------------------------------------------------------------------ +/// @class KillOperation +/// @brief Implements ProcessMonitor::BringProcessIntoLimbo. +class KillOperation : public Operation +{ +public: + KillOperation(bool &result) : m_result(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + bool &m_result; +}; + +void +KillOperation::Execute(ProcessMonitor *monitor) +{ + lldb::pid_t pid = monitor->GetPID(); + + if (PTRACE(PT_KILL, pid, NULL, 0)) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class KillOperation +/// @brief Implements ProcessMonitor::BringProcessIntoLimbo. +class DetachOperation : public Operation +{ +public: + DetachOperation(Error &result) : m_error(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + Error &m_error; +}; + +void +DetachOperation::Execute(ProcessMonitor *monitor) +{ + lldb::pid_t pid = monitor->GetPID(); + + if (PTRACE(PT_DETACH, pid, NULL, 0) < 0) + m_error.SetErrorToErrno(); + +} + +ProcessMonitor::OperationArgs::OperationArgs(ProcessMonitor *monitor) + : m_monitor(monitor) +{ + sem_init(&m_semaphore, 0, 0); +} + +ProcessMonitor::OperationArgs::~OperationArgs() +{ + sem_destroy(&m_semaphore); +} + +ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor, + lldb_private::Module *module, + char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path) + : OperationArgs(monitor), + m_module(module), + m_argv(argv), + m_envp(envp), + m_stdin_path(stdin_path), + m_stdout_path(stdout_path), + m_stderr_path(stderr_path) { } + +ProcessMonitor::LaunchArgs::~LaunchArgs() +{ } + +ProcessMonitor::AttachArgs::AttachArgs(ProcessMonitor *monitor, + lldb::pid_t pid) + : OperationArgs(monitor), m_pid(pid) { } + +ProcessMonitor::AttachArgs::~AttachArgs() +{ } + +//------------------------------------------------------------------------------ +/// The basic design of the ProcessMonitor is built around two threads. +/// +/// One thread (@see SignalThread) simply blocks on a call to waitpid() looking +/// for changes in the debugee state. When a change is detected a +/// ProcessMessage is sent to the associated ProcessFreeBSD instance. This thread +/// "drives" state changes in the debugger. +/// +/// The second thread (@see OperationThread) is responsible for two things 1) +/// launching or attaching to the inferior process, and then 2) servicing +/// operations such as register reads/writes, stepping, etc. See the comments +/// on the Operation class for more info as to why this is needed. +ProcessMonitor::ProcessMonitor(ProcessPOSIX *process, + Module *module, + const char *argv[], + const char *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + lldb_private::Error &error) + : m_process(static_cast<ProcessFreeBSD *>(process)), + m_operation_thread(LLDB_INVALID_HOST_THREAD), + m_monitor_thread(LLDB_INVALID_HOST_THREAD), + m_pid(LLDB_INVALID_PROCESS_ID), + m_server_mutex(Mutex::eMutexTypeRecursive), + m_terminal_fd(-1), + m_client_fd(-1), + m_server_fd(-1) +{ + std::auto_ptr<LaunchArgs> args; + + args.reset(new LaunchArgs(this, module, argv, envp, + stdin_path, stdout_path, stderr_path)); + + + // Server/client descriptors. + if (!EnableIPC()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Monitor failed to initialize."); + } + + StartLaunchOpThread(args.get(), error); + if (!error.Success()) + return; + +WAIT_AGAIN: + // Wait for the operation thread to initialize. + if (sem_wait(&args->m_semaphore)) + { + if (errno == EINTR) + goto WAIT_AGAIN; + else + { + error.SetErrorToErrno(); + return; + } + } + + // Check that the launch was a success. + if (!args->m_error.Success()) + { + StopLaunchOpThread(); + error = args->m_error; + return; + } + + // Finally, start monitoring the child process for change in state. + m_monitor_thread = Host::StartMonitoringChildProcess( + ProcessMonitor::MonitorCallback, this, GetPID(), true); + if (!IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) + { + error.SetErrorToGenericError(); + error.SetErrorString("Process launch failed."); + return; + } +} + +ProcessMonitor::ProcessMonitor(ProcessPOSIX *process, + lldb::pid_t pid, + lldb_private::Error &error) + : m_process(static_cast<ProcessFreeBSD *>(process)), + m_operation_thread(LLDB_INVALID_HOST_THREAD), + m_monitor_thread(LLDB_INVALID_HOST_THREAD), + m_pid(pid), + m_server_mutex(Mutex::eMutexTypeRecursive), + m_terminal_fd(-1), + m_client_fd(-1), + m_server_fd(-1) +{ + std::auto_ptr<AttachArgs> args; + + args.reset(new AttachArgs(this, pid)); + + // Server/client descriptors. + if (!EnableIPC()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Monitor failed to initialize."); + } + + StartAttachOpThread(args.get(), error); + if (!error.Success()) + return; + +WAIT_AGAIN: + // Wait for the operation thread to initialize. + if (sem_wait(&args->m_semaphore)) + { + if (errno == EINTR) + goto WAIT_AGAIN; + else + { + error.SetErrorToErrno(); + return; + } + } + + // Check that the launch was a success. + if (!args->m_error.Success()) + { + StopAttachOpThread(); + error = args->m_error; + return; + } + + // Finally, start monitoring the child process for change in state. + m_monitor_thread = Host::StartMonitoringChildProcess( + ProcessMonitor::MonitorCallback, this, GetPID(), true); + if (!IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) + { + error.SetErrorToGenericError(); + error.SetErrorString("Process attach failed."); + return; + } +} + +ProcessMonitor::~ProcessMonitor() +{ + StopMonitor(); +} + +//------------------------------------------------------------------------------ +// Thread setup and tear down. +void +ProcessMonitor::StartLaunchOpThread(LaunchArgs *args, Error &error) +{ + static const char *g_thread_name = "lldb.process.freebsd.operation"; + + if (IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) + return; + + m_operation_thread = + Host::ThreadCreate(g_thread_name, LaunchOpThread, args, &error); +} + +void +ProcessMonitor::StopLaunchOpThread() +{ + lldb::thread_result_t result; + + if (!IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) + return; + + Host::ThreadCancel(m_operation_thread, NULL); + Host::ThreadJoin(m_operation_thread, &result, NULL); +} + +void * +ProcessMonitor::LaunchOpThread(void *arg) +{ + LaunchArgs *args = static_cast<LaunchArgs*>(arg); + + if (!Launch(args)) { + sem_post(&args->m_semaphore); + return NULL; + } + + ServeOperation(args); + return NULL; +} + +bool +ProcessMonitor::Launch(LaunchArgs *args) +{ + ProcessMonitor *monitor = args->m_monitor; + ProcessFreeBSD &process = monitor->GetProcess(); + const char **argv = args->m_argv; + const char **envp = args->m_envp; + const char *stdin_path = args->m_stdin_path; + const char *stdout_path = args->m_stdout_path; + const char *stderr_path = args->m_stderr_path; + lldb::pid_t pid; + + lldb::ThreadSP inferior; + + // Propagate the environment if one is not supplied. + if (envp == NULL || envp[0] == NULL) + envp = const_cast<const char **>(environ); + + // Recognized child exit status codes. + enum { + ePtraceFailed = 1, + eDupStdinFailed, + eDupStdoutFailed, + eDupStderrFailed, + eExecFailed + }; + + pid = fork(); + + // Child process. + if (pid == 0) + { + // Trace this process. + if (PTRACE(PT_TRACE_ME, 0, NULL, 0) < 0) + exit(ePtraceFailed); + + // Do not inherit setgid powers. + setgid(getgid()); + + // Let us have our own process group. + setpgid(0, 0); + + // Dup file descriptors if needed. + // + // FIXME: If two or more of the paths are the same we needlessly open + // the same file multiple times. + if (stdin_path != NULL && stdin_path[0]) + if (!DupDescriptor(stdin_path, STDIN_FILENO, O_RDONLY)) + exit(eDupStdinFailed); + + if (stdout_path != NULL && stdout_path[0]) + if (!DupDescriptor(stdout_path, STDOUT_FILENO, O_WRONLY | O_CREAT)) + exit(eDupStdoutFailed); + + if (stderr_path != NULL && stderr_path[0]) + if (!DupDescriptor(stderr_path, STDERR_FILENO, O_WRONLY | O_CREAT)) + exit(eDupStderrFailed); + + // Execute. We should never return. + execve(argv[0], + const_cast<char *const *>(argv), + const_cast<char *const *>(envp)); + exit(eExecFailed); + } + + // Wait for the child process to to trap on its call to execve. + ::pid_t wpid; + int status; + if ((wpid = waitpid(pid, &status, 0)) < 0) + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } + else if (WIFEXITED(status)) + { + // open, dup or execve likely failed for some reason. + args->m_error.SetErrorToGenericError(); + switch (WEXITSTATUS(status)) + { + case ePtraceFailed: + args->m_error.SetErrorString("Child ptrace failed."); + break; + case eDupStdinFailed: + args->m_error.SetErrorString("Child open stdin failed."); + break; + case eDupStdoutFailed: + args->m_error.SetErrorString("Child open stdout failed."); + break; + case eDupStderrFailed: + args->m_error.SetErrorString("Child open stderr failed."); + break; + case eExecFailed: + args->m_error.SetErrorString("Child exec failed."); + break; + default: + args->m_error.SetErrorString("Child returned unknown exit status."); + break; + } + goto FINISH; + } + assert(WIFSTOPPED(status) && wpid == pid && + "Could not sync with inferior process."); + +#ifdef notyet + // Have the child raise an event on exit. This is used to keep the child in + // limbo until it is destroyed. + if (PTRACE(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0) + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } +#endif + // XXX - Release the master terminal descriptor and pass it off to the + // XXX - ProcessMonitor instance. Similarly stash the inferior pid. + // For now just use stdin fd + monitor->m_terminal_fd = STDIN_FILENO; + monitor->m_pid = pid; + + // Set the terminal fd to be in non blocking mode (it simplifies the + // implementation of ProcessFreeBSD::GetSTDOUT to have a non-blocking + // descriptor to read from). + if (!EnsureFDFlags(monitor->m_terminal_fd, O_NONBLOCK, args->m_error)) + goto FINISH; + + // Update the process thread list with this new thread. + inferior.reset(new POSIXThread(process, pid)); + process.GetThreadList().AddThread(inferior); + + // Let our process instance know the thread has stopped. + process.SendMessage(ProcessMessage::Trace(pid)); + +FINISH: + return args->m_error.Success(); +} + +bool +ProcessMonitor::EnableIPC() +{ + int fd[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) + return false; + + m_client_fd = fd[0]; + m_server_fd = fd[1]; + return true; +} + +void +ProcessMonitor::StartAttachOpThread(AttachArgs *args, lldb_private::Error &error) +{ + static const char *g_thread_name = "lldb.process.freebsd.operation"; + + if (IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) + return; + + m_operation_thread = + Host::ThreadCreate(g_thread_name, AttachOpThread, args, &error); +} + +void +ProcessMonitor::StopAttachOpThread() +{ + assert(!"Not implemented yet!!!"); +} + +void * +ProcessMonitor::AttachOpThread(void *arg) +{ + AttachArgs *args = static_cast<AttachArgs*>(arg); + + if (!Attach(args)) + return NULL; + + ServeOperation(args); + return NULL; +} + +bool +ProcessMonitor::Attach(AttachArgs *args) +{ + lldb::pid_t pid = args->m_pid; + + ProcessMonitor *monitor = args->m_monitor; + ProcessFreeBSD &process = monitor->GetProcess(); + ThreadList &tl = process.GetThreadList(); + lldb::ThreadSP inferior; + + if (pid <= 1) + { + args->m_error.SetErrorToGenericError(); + args->m_error.SetErrorString("Attaching to process 1 is not allowed."); + goto FINISH; + } + + // Attach to the requested process. + if (PTRACE(PT_ATTACH, pid, NULL, 0) < 0) + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } + + int status; + if ((status = waitpid(pid, NULL, 0)) < 0) + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } + + // Update the process thread list with the attached thread. + inferior.reset(new POSIXThread(process, pid)); + tl.AddThread(inferior); + + // Let our process instance know the thread has stopped. + process.SendMessage(ProcessMessage::Trace(pid)); + + FINISH: + return args->m_error.Success(); +} + +bool +ProcessMonitor::MonitorCallback(void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, + int status) +{ + ProcessMessage message; + ProcessMonitor *monitor = static_cast<ProcessMonitor*>(callback_baton); + ProcessFreeBSD *process = monitor->m_process; + bool stop_monitoring; + siginfo_t info; + + if (!monitor->GetSignalInfo(pid, &info)) + stop_monitoring = true; // pid is gone. Bail. + else { + switch (info.si_signo) + { + case SIGTRAP: + message = MonitorSIGTRAP(monitor, &info, pid); + break; + + default: + message = MonitorSignal(monitor, &info, pid); + break; + } + + process->SendMessage(message); + stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage; + } + + return stop_monitoring; +} + +ProcessMessage +ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, + const siginfo_t *info, lldb::pid_t pid) +{ + ProcessMessage message; + + assert(info->si_signo == SIGTRAP && "Unexpected child signal!"); + + switch (info->si_code) + { + default: + assert(false && "Unexpected SIGTRAP code!"); + break; + + case (SIGTRAP /* | (PTRACE_EVENT_EXIT << 8) */): + { + // The inferior process is about to exit. Maintain the process in a + // state of "limbo" until we are explicitly commanded to detach, + // destroy, resume, etc. + unsigned long data = 0; + if (!monitor->GetEventMessage(pid, &data)) + data = -1; + message = ProcessMessage::Limbo(pid, (data >> 8)); + break; + } + + case 0: + case TRAP_TRACE: + message = ProcessMessage::Trace(pid); + break; + + case SI_KERNEL: + case TRAP_BRKPT: + message = ProcessMessage::Break(pid); + break; + } + + return message; +} + +ProcessMessage +ProcessMonitor::MonitorSignal(ProcessMonitor *monitor, + const siginfo_t *info, lldb::pid_t pid) +{ + ProcessMessage message; + int signo = info->si_signo; + + // POSIX says that process behaviour is undefined after it ignores a SIGFPE, + // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a + // kill(2) or raise(3). Similarly for tgkill(2) on FreeBSD. + // + // IOW, user generated signals never generate what we consider to be a + // "crash". + // + // Similarly, ACK signals generated by this monitor. + if (info->si_code == SI_USER) + { + if (info->si_pid == getpid()) + return ProcessMessage::SignalDelivered(pid, signo); + else + return ProcessMessage::Signal(pid, signo); + } + + if (signo == SIGSEGV) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGSEGV(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + if (signo == SIGILL) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGILL(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + if (signo == SIGFPE) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGFPE(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + if (signo == SIGBUS) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGBUS(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + // Everything else is "normal" and does not require any special action on + // our part. + return ProcessMessage::Signal(pid, signo); +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGSEGV(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGSEGV); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGSEGV"); + break; + case SEGV_MAPERR: + reason = ProcessMessage::eInvalidAddress; + break; + case SEGV_ACCERR: + reason = ProcessMessage::ePrivilegedAddress; + break; + } + + return reason; +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGILL(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGILL); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGILL"); + break; + case ILL_ILLOPC: + reason = ProcessMessage::eIllegalOpcode; + break; + case ILL_ILLOPN: + reason = ProcessMessage::eIllegalOperand; + break; + case ILL_ILLADR: + reason = ProcessMessage::eIllegalAddressingMode; + break; + case ILL_ILLTRP: + reason = ProcessMessage::eIllegalTrap; + break; + case ILL_PRVOPC: + reason = ProcessMessage::ePrivilegedOpcode; + break; + case ILL_PRVREG: + reason = ProcessMessage::ePrivilegedRegister; + break; + case ILL_COPROC: + reason = ProcessMessage::eCoprocessorError; + break; + case ILL_BADSTK: + reason = ProcessMessage::eInternalStackError; + break; + } + + return reason; +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGFPE(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGFPE); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGFPE"); + break; + case FPE_INTDIV: + reason = ProcessMessage::eIntegerDivideByZero; + break; + case FPE_INTOVF: + reason = ProcessMessage::eIntegerOverflow; + break; + case FPE_FLTDIV: + reason = ProcessMessage::eFloatDivideByZero; + break; + case FPE_FLTOVF: + reason = ProcessMessage::eFloatOverflow; + break; + case FPE_FLTUND: + reason = ProcessMessage::eFloatUnderflow; + break; + case FPE_FLTRES: + reason = ProcessMessage::eFloatInexactResult; + break; + case FPE_FLTINV: + reason = ProcessMessage::eFloatInvalidOperation; + break; + case FPE_FLTSUB: + reason = ProcessMessage::eFloatSubscriptRange; + break; + } + + return reason; +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGBUS(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGBUS); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGBUS"); + break; + case BUS_ADRALN: + reason = ProcessMessage::eIllegalAlignment; + break; + case BUS_ADRERR: + reason = ProcessMessage::eIllegalAddress; + break; + case BUS_OBJERR: + reason = ProcessMessage::eHardwareError; + break; + } + + return reason; +} + +void +ProcessMonitor::ServeOperation(OperationArgs *args) +{ + int status; + pollfd fdset; + + ProcessMonitor *monitor = args->m_monitor; + + fdset.fd = monitor->m_server_fd; + fdset.events = POLLIN | POLLPRI; + fdset.revents = 0; + + // We are finised with the arguments and are ready to go. Sync with the + // parent thread and start serving operations on the inferior. + sem_post(&args->m_semaphore); + + for (;;) + { + if ((status = poll(&fdset, 1, -1)) < 0) + { + switch (errno) + { + default: + assert(false && "Unexpected poll() failure!"); + continue; + + case EINTR: continue; // Just poll again. + case EBADF: return; // Connection terminated. + } + } + + assert(status == 1 && "Too many descriptors!"); + + if (fdset.revents & POLLIN) + { + Operation *op = NULL; + + READ_AGAIN: + if ((status = read(fdset.fd, &op, sizeof(op))) < 0) + { + // There is only one acceptable failure. + assert(errno == EINTR); + goto READ_AGAIN; + } + + assert(status == sizeof(op)); + op->Execute(monitor); + write(fdset.fd, &op, sizeof(op)); + } + } +} + +void +ProcessMonitor::DoOperation(Operation *op) +{ + int status; + Operation *ack = NULL; + Mutex::Locker lock(m_server_mutex); + + // FIXME: Do proper error checking here. + write(m_client_fd, &op, sizeof(op)); + +READ_AGAIN: + if ((status = read(m_client_fd, &ack, sizeof(ack))) < 0) + { + // If interrupted by a signal handler try again. Otherwise the monitor + // thread probably died and we have a stale file descriptor -- abort the + // operation. + if (errno == EINTR) + goto READ_AGAIN; + return; + } + + assert(status == sizeof(ack)); + assert(ack == op && "Invalid monitor thread response!"); +} + +size_t +ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Error &error) +{ + size_t result; + ReadOperation op(vm_addr, buf, size, error, result); + DoOperation(&op); + return result; +} + +size_t +ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, + lldb_private::Error &error) +{ + size_t result; + WriteOperation op(vm_addr, buf, size, error, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::ReadRegisterValue(unsigned offset, unsigned size, RegisterValue &value) +{ + bool result; + ReadRegOperation op(offset, size, value, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::WriteRegisterValue(unsigned offset, const RegisterValue &value) +{ + bool result; + WriteRegOperation op(offset, value, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::ReadGPR(void *buf) +{ + bool result; + ReadGPROperation op(buf, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::ReadFPR(void *buf) +{ + bool result; + ReadFPROperation op(buf, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::WriteGPR(void *buf) +{ + bool result; + WriteGPROperation op(buf, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::WriteFPR(void *buf) +{ + bool result; + WriteFPROperation op(buf, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::Resume(lldb::tid_t tid, uint32_t signo) +{ + bool result; + ResumeOperation op(tid, signo, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::SingleStep(lldb::tid_t tid, uint32_t signo) +{ + bool result; + SingleStepOperation op(tid, signo, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::BringProcessIntoLimbo() +{ + bool result; + KillOperation op(result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::GetSignalInfo(lldb::tid_t tid, void *siginfo) +{ + bool result; + SiginfoOperation op(tid, siginfo, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) +{ + bool result; + EventMessageOperation op(tid, message, result); + DoOperation(&op); + return result; +} + +Error +ProcessMonitor::Detach() +{ + Error result; + DetachOperation op(result); + DoOperation(&op); + StopMonitor(); + return result; +} + +bool +ProcessMonitor::DupDescriptor(const char *path, int fd, int flags) +{ + int target_fd = open(path, flags, 0666); + + if (target_fd == -1) + return false; + + return (dup2(target_fd, fd) == -1) ? false : true; +} + +void +ProcessMonitor::StopMonitoringChildProcess() +{ + lldb::thread_result_t thread_result; + + if (IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) + { + Host::ThreadCancel(m_monitor_thread, NULL); + Host::ThreadJoin(m_monitor_thread, &thread_result, NULL); + m_monitor_thread = LLDB_INVALID_HOST_THREAD; + } +} + +void +ProcessMonitor::StopMonitor() +{ + StopMonitoringChildProcess(); + StopLaunchOpThread(); + CloseFD(m_terminal_fd); + CloseFD(m_client_fd); + CloseFD(m_server_fd); +} + +void +ProcessMonitor::CloseFD(int &fd) +{ + if (fd != -1) + { + close(fd); + fd = -1; + } +} diff --git a/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h b/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h new file mode 100644 index 00000000000..9c630049caf --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h @@ -0,0 +1,292 @@ +//===-- ProcessMonitor.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_ProcessMonitor_H_ +#define liblldb_ProcessMonitor_H_ + +// C Includes +#include <semaphore.h> +#include <signal.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/lldb-types.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private +{ +class Error; +class Module; +class Scalar; +} // End lldb_private namespace. + +class ProcessFreeBSD; +class Operation; + +/// @class ProcessMonitor +/// @brief Manages communication with the inferior (debugee) process. +/// +/// Upon construction, this class prepares and launches an inferior process for +/// debugging. +/// +/// Changes in the inferior process state are propagated to the associated +/// ProcessFreeBSD instance by calling ProcessFreeBSD::SendMessage with the +/// appropriate ProcessMessage events. +/// +/// A purposely minimal set of operations are provided to interrogate and change +/// the inferior process state. +class ProcessMonitor +{ +public: + + /// Launches an inferior process ready for debugging. Forms the + /// implementation of Process::DoLaunch. + ProcessMonitor(ProcessPOSIX *process, + lldb_private::Module *module, + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + lldb_private::Error &error); + + ProcessMonitor(ProcessPOSIX *process, + lldb::pid_t pid, + lldb_private::Error &error); + + ~ProcessMonitor(); + + /// Provides the process number of debugee. + lldb::pid_t + GetPID() const { return m_pid; } + + /// Returns the process associated with this ProcessMonitor. + ProcessFreeBSD & + GetProcess() { return *m_process; } + + /// Returns a file descriptor to the controlling terminal of the inferior + /// process. + /// + /// Reads from this file descriptor yield both the standard output and + /// standard error of this debugee. Even if stderr and stdout were + /// redirected on launch it may still happen that data is available on this + /// descriptor (if the inferior process opens /dev/tty, for example). + /// + /// If this monitor was attached to an existing process this method returns + /// -1. + int + GetTerminalFD() const { return m_terminal_fd; } + + /// Reads @p size bytes from address @vm_adder in the inferior process + /// address space. + /// + /// This method is provided to implement Process::DoReadMemory. + size_t + ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + lldb_private::Error &error); + + /// Writes @p size bytes from address @p vm_adder in the inferior process + /// address space. + /// + /// This method is provided to implement Process::DoWriteMemory. + size_t + WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, + lldb_private::Error &error); + + /// Reads the contents from the register identified by the given (architecture + /// dependent) offset. + /// + /// This method is provided for use by RegisterContextFreeBSD derivatives. + bool + ReadRegisterValue(unsigned offset, unsigned size, lldb_private::RegisterValue &value); + + /// Writes the given value to the register identified by the given + /// (architecture dependent) offset. + /// + /// This method is provided for use by RegisterContextFreeBSD derivatives. + bool + WriteRegisterValue(unsigned offset, const lldb_private::RegisterValue &value); + + /// Reads all general purpose registers into the specified buffer. + bool + ReadGPR(void *buf); + + /// Reads all floating point registers into the specified buffer. + bool + ReadFPR(void *buf); + + /// Writes all general purpose registers into the specified buffer. + bool + WriteGPR(void *buf); + + /// Writes all floating point registers into the specified buffer. + bool + WriteFPR(void *buf); + + /// Writes a siginfo_t structure corresponding to the given thread ID to the + /// memory region pointed to by @p siginfo. + bool + GetSignalInfo(lldb::tid_t tid, void *siginfo); + + /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) + /// corresponding to the given thread IDto the memory pointed to by @p + /// message. + bool + GetEventMessage(lldb::tid_t tid, unsigned long *message); + + /// Resumes the given thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + bool + Resume(lldb::tid_t tid, uint32_t signo); + + /// Single steps the given thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + bool + SingleStep(lldb::tid_t tid, uint32_t signo); + + /// Sends the inferior process a PTRACE_KILL signal. The inferior will + /// still exists and can be interrogated. Once resumed it will exit as + /// though it received a SIGKILL. + bool + BringProcessIntoLimbo(); + + lldb_private::Error + Detach(); + + +private: + ProcessFreeBSD *m_process; + + lldb::thread_t m_operation_thread; + lldb::thread_t m_monitor_thread; + lldb::pid_t m_pid; + + + lldb_private::Mutex m_server_mutex; + int m_terminal_fd; + int m_client_fd; + int m_server_fd; + + struct OperationArgs + { + OperationArgs(ProcessMonitor *monitor); + + ~OperationArgs(); + + ProcessMonitor *m_monitor; // The monitor performing the attach. + sem_t m_semaphore; // Posted to once operation complete. + lldb_private::Error m_error; // Set if process operation failed. + }; + + /// @class LauchArgs + /// + /// @brief Simple structure to pass data to the thread responsible for + /// launching a child process. + struct LaunchArgs : OperationArgs + { + LaunchArgs(ProcessMonitor *monitor, + lldb_private::Module *module, + char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path); + + ~LaunchArgs(); + + lldb_private::Module *m_module; // The executable image to launch. + char const **m_argv; // Process arguments. + char const **m_envp; // Process environment. + const char *m_stdin_path; // Redirect stdin or NULL. + const char *m_stdout_path; // Redirect stdout or NULL. + const char *m_stderr_path; // Redirect stderr or NULL. + }; + + void + StartLaunchOpThread(LaunchArgs *args, lldb_private::Error &error); + + void + StopLaunchOpThread(); + + static void * + LaunchOpThread(void *arg); + + static bool + Launch(LaunchArgs *args); + + bool + EnableIPC(); + + struct AttachArgs : OperationArgs + { + AttachArgs(ProcessMonitor *monitor, + lldb::pid_t pid); + + ~AttachArgs(); + + lldb::pid_t m_pid; // pid of the process to be attached. + }; + + void + StartAttachOpThread(AttachArgs *args, lldb_private::Error &error); + + void + StopAttachOpThread(); + + static void * + AttachOpThread(void *args); + + static bool + Attach(AttachArgs *args); + + static void + ServeOperation(OperationArgs *args); + + static bool + DupDescriptor(const char *path, int fd, int flags); + + static bool + MonitorCallback(void *callback_baton, + lldb::pid_t pid, bool exited, int signal, int status); + + static ProcessMessage + MonitorSIGTRAP(ProcessMonitor *monitor, + const siginfo_t *info, lldb::pid_t pid); + + static ProcessMessage + MonitorSignal(ProcessMonitor *monitor, + const siginfo_t *info, lldb::pid_t pid); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGSEGV(const siginfo_t *info); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGILL(const siginfo_t *info); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGFPE(const siginfo_t *info); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGBUS(const siginfo_t *info); + + void + DoOperation(Operation *op); + + /// Stops the child monitor thread. + void + StopMonitoringChildProcess(); + + void + StopMonitor(); + + void + CloseFD(int &fd); +}; + +#endif // #ifndef liblldb_ProcessMonitor_H_ diff --git a/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.h b/lldb/source/Plugins/Process/FreeBSD/RegisterContextFreeBSD_x86_64.h index 3dc6c7298da..25590e74c09 100644 --- a/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.h +++ b/lldb/source/Plugins/Process/FreeBSD/RegisterContextFreeBSD_x86_64.h @@ -1,4 +1,4 @@ -//===-- RegisterContextLinux_x86_64.h ---------------------------*- C++ -*-===// +//===-- RegisterContextFreeBSD_x86_64.h ---------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef liblldb_RegisterContextLinux_x86_64_H_ -#define liblldb_RegisterContextLinux_x86_64_H_ +#ifndef liblldb_RegisterContextFreeBSD_x86_64_H_ +#define liblldb_RegisterContextFreeBSD_x86_64_H_ typedef struct _GPR { @@ -16,29 +16,28 @@ uint64_t r14; uint64_t r13; uint64_t r12; - uint64_t rbp; - uint64_t rbx; uint64_t r11; uint64_t r10; uint64_t r9; uint64_t r8; - uint64_t rax; - uint64_t rcx; - uint64_t rdx; - uint64_t rsi; uint64_t rdi; - uint64_t orig_ax; + uint64_t rsi; + uint64_t rbp; + uint64_t rbx; + uint64_t rdx; + uint64_t rcx; + uint64_t rax; + uint32_t trapno; + uint16_t fs; + uint16_t gs; + uint32_t err; + uint16_t es; + uint16_t ds; uint64_t rip; uint64_t cs; uint64_t rflags; uint64_t rsp; uint64_t ss; - uint64_t fs_base; - uint64_t gs_base; - uint64_t ds; - uint64_t es; - uint64_t fs; - uint64_t gs; } GPR; #endif diff --git a/lldb/source/Plugins/Process/Linux/LinuxStopInfo.cpp b/lldb/source/Plugins/Process/Linux/LinuxStopInfo.cpp deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/LinuxStopInfo.cpp +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/LinuxStopInfo.h b/lldb/source/Plugins/Process/Linux/LinuxStopInfo.h deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/LinuxStopInfo.h +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/LinuxThread.cpp b/lldb/source/Plugins/Process/Linux/LinuxThread.cpp deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/LinuxThread.cpp +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/LinuxThread.h b/lldb/source/Plugins/Process/Linux/LinuxThread.h deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/LinuxThread.h +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinuxLog.cpp b/lldb/source/Plugins/Process/Linux/ProcessLinuxLog.cpp deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/ProcessLinuxLog.cpp +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinuxLog.h b/lldb/source/Plugins/Process/Linux/ProcessLinuxLog.h deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/ProcessLinuxLog.h +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/ProcessMessage.cpp b/lldb/source/Plugins/Process/Linux/ProcessMessage.cpp deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/ProcessMessage.cpp +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/ProcessMessage.h b/lldb/source/Plugins/Process/Linux/ProcessMessage.h deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/ProcessMessage.h +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/RegisterContextLinux.h b/lldb/source/Plugins/Process/Linux/RegisterContextLinux.h deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/RegisterContextLinux.h +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/RegisterContextLinux_i386.cpp b/lldb/source/Plugins/Process/Linux/RegisterContextLinux_i386.cpp deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/RegisterContextLinux_i386.cpp +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/RegisterContextLinux_i386.h b/lldb/source/Plugins/Process/Linux/RegisterContextLinux_i386.h deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/RegisterContextLinux_i386.h +++ /dev/null diff --git a/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.cpp b/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.cpp deleted file mode 100644 index e69de29bb2d..00000000000 --- a/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.cpp +++ /dev/null diff --git a/lldb/source/Plugins/Process/POSIX/Makefile b/lldb/source/Plugins/Process/POSIX/Makefile new file mode 100644 index 00000000000..6705b0d8d16 --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/Makefile @@ -0,0 +1,28 @@ +##===- source/Plugins/Process/POSIX/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 := lldbPluginProcessPOSIX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/../../Makefile.config + +# Extend the include path so we may locate UnwindLLDB.h +CPPFLAGS += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility + +ifeq ($(HOST_OS),Linux) +CPPFLAGS += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/Linux +endif + +ifeq ($(HOST_OS),FreeBSD) +# Extend the include path so we may locate ProcessMonitor +CPPFLAGS += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/FreeBSD +endif + +include $(LLDB_LEVEL)/Makefile diff --git a/lldb/source/Plugins/Process/POSIX/POSIXStopInfo.cpp b/lldb/source/Plugins/Process/POSIX/POSIXStopInfo.cpp new file mode 100644 index 00000000000..79f0a58115b --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/POSIXStopInfo.cpp @@ -0,0 +1,60 @@ +//===-- POSIXStopInfo.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "POSIXStopInfo.h" + +using namespace lldb; +using namespace lldb_private; + + +//===----------------------------------------------------------------------===// +// POSIXLimboStopInfo + +POSIXLimboStopInfo::~POSIXLimboStopInfo() { } + +lldb::StopReason +POSIXLimboStopInfo::GetStopReason() const +{ + return lldb::eStopReasonTrace; +} + +const char * +POSIXLimboStopInfo::GetDescription() +{ + return "thread exiting"; +} + +bool +POSIXLimboStopInfo::ShouldStop(Event *event_ptr) +{ + return true; +} + +bool +POSIXLimboStopInfo::ShouldNotify(Event *event_ptr) +{ + return true; +} + +//===----------------------------------------------------------------------===// +// POSIXCrashStopInfo + +POSIXCrashStopInfo::~POSIXCrashStopInfo() { } + +lldb::StopReason +POSIXCrashStopInfo::GetStopReason() const +{ + return lldb::eStopReasonException; +} + +const char * +POSIXCrashStopInfo::GetDescription() +{ + return ProcessMessage::GetCrashReasonString(m_crash_reason); +} diff --git a/lldb/source/Plugins/Process/POSIX/POSIXStopInfo.h b/lldb/source/Plugins/Process/POSIX/POSIXStopInfo.h new file mode 100644 index 00000000000..c510377f462 --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/POSIXStopInfo.h @@ -0,0 +1,92 @@ +//===-- POSIXStopInfo.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_POSIXStopInfo_H_ +#define liblldb_POSIXStopInfo_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/StopInfo.h" + +#include "POSIXThread.h" +#include "ProcessMessage.h" + +//===----------------------------------------------------------------------===// +/// @class POSIXStopInfo +/// @brief Simple base class for all POSIX-specific StopInfo objects. +/// +class POSIXStopInfo + : public lldb_private::StopInfo +{ +public: + POSIXStopInfo(lldb_private::Thread &thread, uint32_t status) + : StopInfo(thread, status) + { } +}; + +//===----------------------------------------------------------------------===// +/// @class POSIXLimboStopInfo +/// @brief Represents the stop state of a process ready to exit. +/// +class POSIXLimboStopInfo + : public POSIXStopInfo +{ +public: + POSIXLimboStopInfo(POSIXThread &thread) + : POSIXStopInfo(thread, 0) + { } + + ~POSIXLimboStopInfo(); + + lldb::StopReason + GetStopReason() const; + + const char * + GetDescription(); + + bool + ShouldStop(lldb_private::Event *event_ptr); + + bool + ShouldNotify(lldb_private::Event *event_ptr); +}; + + +//===----------------------------------------------------------------------===// +/// @class POSIXCrashStopInfo +/// @brief Represents the stop state of process that is ready to crash. +/// +class POSIXCrashStopInfo + : public POSIXStopInfo +{ +public: + POSIXCrashStopInfo(POSIXThread &thread, uint32_t status, + ProcessMessage::CrashReason reason) + : POSIXStopInfo(thread, status), + m_crash_reason(reason) + { } + + ~POSIXCrashStopInfo(); + + lldb::StopReason + GetStopReason() const; + + const char * + GetDescription(); + + ProcessMessage::CrashReason + GetCrashReason() const; + +private: + ProcessMessage::CrashReason m_crash_reason; +}; + +#endif diff --git a/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp b/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp new file mode 100644 index 00000000000..3f5e022ee30 --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp @@ -0,0 +1,352 @@ +//===-- POSIXThread.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 <errno.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "POSIXStopInfo.h" +#include "POSIXThread.h" +#include "ProcessPOSIX.h" +#include "ProcessPOSIXLog.h" +#include "ProcessMonitor.h" +#include "RegisterContext_i386.h" +#include "RegisterContext_x86_64.h" +#include "RegisterContextPOSIX.h" + +#include "UnwindLLDB.h" + +using namespace lldb_private; + + +POSIXThread::POSIXThread(Process &process, lldb::tid_t tid) + : Thread(process, tid), + m_frame_ap(0) +{ + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("POSIXThread::%s (tid = %i)", __FUNCTION__, tid); +} + +POSIXThread::~POSIXThread() +{ + DestroyThread(); +} + +ProcessMonitor & +POSIXThread::GetMonitor() +{ + ProcessPOSIX &process = static_cast<ProcessPOSIX&>(GetProcess()); + return process.GetMonitor(); +} + +void +POSIXThread::RefreshStateAfterStop() +{ + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("POSIXThread::%s ()", __FUNCTION__); + + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + ProcessPOSIX &process = static_cast<ProcessPOSIX&>(GetProcess()); + process.GetThreadList().RefreshStateAfterStop(); +} + +const char * +POSIXThread::GetInfo() +{ + return NULL; +} + +lldb::RegisterContextSP +POSIXThread::GetRegisterContext() +{ + if (!m_reg_context_sp) + { + ArchSpec arch = Host::GetArchitecture(); + + switch (arch.GetCore()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + m_reg_context_sp.reset(new RegisterContext_i386(*this, 0)); + break; + + case ArchSpec::eCore_x86_64_x86_64: + m_reg_context_sp.reset(new RegisterContext_x86_64(*this, 0)); + break; + } + } + return m_reg_context_sp; +} + +lldb::RegisterContextSP +POSIXThread::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("POSIXThread::%s ()", __FUNCTION__); + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) + reg_ctx_sp = GetRegisterContext(); + else + { + assert(GetUnwinder()); + reg_ctx_sp = GetUnwinder()->CreateRegisterContextForFrame(frame); + } + + return reg_ctx_sp; +} + +lldb::StopInfoSP +POSIXThread::GetPrivateStopReason() +{ + return m_stop_info; +} + +Unwind * +POSIXThread::GetUnwinder() +{ + if (m_unwinder_ap.get() == NULL) + m_unwinder_ap.reset(new UnwindLLDB(*this)); + + return m_unwinder_ap.get(); +} + +bool +POSIXThread::WillResume(lldb::StateType resume_state) +{ + SetResumeState(resume_state); + + ClearStackFrames(); + if (m_unwinder_ap.get()) + m_unwinder_ap->Clear(); + + return Thread::WillResume(resume_state); +} + +bool +POSIXThread::Resume() +{ + lldb::StateType resume_state = GetResumeState(); + ProcessMonitor &monitor = GetMonitor(); + bool status; + + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("POSIXThread::%s ()", __FUNCTION__); + + switch (resume_state) + { + default: + assert(false && "Unexpected state for resume!"); + status = false; + break; + + case lldb::eStateRunning: + SetState(resume_state); + status = monitor.Resume(GetID(), GetResumeSignal()); + break; + + case lldb::eStateStepping: + SetState(resume_state); + status = monitor.SingleStep(GetID(), GetResumeSignal()); + break; + } + + return status; +} + +void +POSIXThread::Notify(const ProcessMessage &message) +{ + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log) + log->Printf ("POSIXThread::%s () message kind = '%s'", __FUNCTION__, message.PrintKind()); + + switch (message.GetKind()) + { + default: + assert(false && "Unexpected message kind!"); + break; + + case ProcessMessage::eLimboMessage: + LimboNotify(message); + break; + + case ProcessMessage::eSignalMessage: + SignalNotify(message); + break; + + case ProcessMessage::eSignalDeliveredMessage: + SignalDeliveredNotify(message); + break; + + case ProcessMessage::eTraceMessage: + TraceNotify(message); + break; + + case ProcessMessage::eBreakpointMessage: + BreakNotify(message); + break; + + case ProcessMessage::eCrashMessage: + CrashNotify(message); + break; + } +} + +void +POSIXThread::BreakNotify(const ProcessMessage &message) +{ + bool status; + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + + assert(GetRegisterContext()); + status = GetRegisterContextPOSIX()->UpdateAfterBreakpoint(); + assert(status && "Breakpoint update failed!"); + + // With our register state restored, resolve the breakpoint object + // corresponding to our current PC. + assert(GetRegisterContext()); + lldb::addr_t pc = GetRegisterContext()->GetPC(); + if (log) + log->Printf ("POSIXThread::%s () PC=0x%8.8llx", __FUNCTION__, pc); + lldb::BreakpointSiteSP bp_site(GetProcess().GetBreakpointSiteList().FindByAddress(pc)); + assert(bp_site); + lldb::break_id_t bp_id = bp_site->GetID(); + assert(bp_site && bp_site->ValidForThisThread(this)); + + + m_breakpoint = bp_site; + m_stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id); +} + +void +POSIXThread::TraceNotify(const ProcessMessage &message) +{ + m_stop_info = StopInfo::CreateStopReasonToTrace(*this); +} + +void +POSIXThread::LimboNotify(const ProcessMessage &message) +{ + m_stop_info = lldb::StopInfoSP(new POSIXLimboStopInfo(*this)); +} + +void +POSIXThread::SignalNotify(const ProcessMessage &message) +{ + int signo = message.GetSignal(); + + m_stop_info = StopInfo::CreateStopReasonWithSignal(*this, signo); + SetResumeSignal(signo); +} + +void +POSIXThread::SignalDeliveredNotify(const ProcessMessage &message) +{ + int signo = message.GetSignal(); + + // Just treat debugger generated signal events like breakpoints for now. + m_stop_info = StopInfo::CreateStopReasonToTrace(*this); + SetResumeSignal(signo); +} + +void +POSIXThread::CrashNotify(const ProcessMessage &message) +{ + int signo = message.GetSignal(); + + assert(message.GetKind() == ProcessMessage::eCrashMessage); + + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log) + log->Printf ("POSIXThread::%s () signo = %i, reason = '%s'", __FUNCTION__, signo, message.PrintCrashReason()); + + m_stop_info = lldb::StopInfoSP(new POSIXCrashStopInfo( + *this, signo, message.GetCrashReason())); + SetResumeSignal(signo); +} + +unsigned +POSIXThread::GetRegisterIndexFromOffset(unsigned offset) +{ + unsigned reg; + ArchSpec arch = Host::GetArchitecture(); + + switch (arch.GetCore()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + reg = RegisterContext_i386::GetRegisterIndexFromOffset(offset); + break; + + case ArchSpec::eCore_x86_64_x86_64: + reg = RegisterContext_x86_64::GetRegisterIndexFromOffset(offset); + break; + } + return reg; +} + +const char * +POSIXThread::GetRegisterName(unsigned reg) +{ + const char * name; + ArchSpec arch = Host::GetArchitecture(); + + switch (arch.GetCore()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + name = RegisterContext_i386::GetRegisterName(reg); + break; + + case ArchSpec::eCore_x86_64_x86_64: + name = RegisterContext_x86_64::GetRegisterName(reg); + break; + } + return name; +} + +const char * +POSIXThread::GetRegisterNameFromOffset(unsigned offset) +{ + return GetRegisterName(GetRegisterIndexFromOffset(offset)); +} + diff --git a/lldb/source/Plugins/Process/POSIX/POSIXThread.h b/lldb/source/Plugins/Process/POSIX/POSIXThread.h new file mode 100644 index 00000000000..95280d46bc5 --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/POSIXThread.h @@ -0,0 +1,106 @@ +//===-- POSIXThread.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_POSIXThread_H_ +#define liblldb_POSIXThread_H_ + +// C Includes +// C++ Includes +#include <memory> + +// Other libraries and framework includes +#include "lldb/Target/Thread.h" +#include "RegisterContextPOSIX.h" + +class ProcessMessage; +class ProcessMonitor; +class RegisterContextPOSIX; + +//------------------------------------------------------------------------------ +// @class POSIXThread +// @brief Abstraction of a linux process (thread). +class POSIXThread + : public lldb_private::Thread +{ +public: + POSIXThread(lldb_private::Process &process, lldb::tid_t tid); + + virtual ~POSIXThread(); + + void + RefreshStateAfterStop(); + + bool + WillResume(lldb::StateType resume_state); + + const char * + GetInfo(); + + virtual lldb::RegisterContextSP + GetRegisterContext(); + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + //-------------------------------------------------------------------------- + // These static functions provide a mapping from the register offset + // back to the register index or name for use in debugging or log + // output. + + static unsigned + GetRegisterIndexFromOffset(unsigned offset); + + static const char * + GetRegisterName(unsigned reg); + + static const char * + GetRegisterNameFromOffset(unsigned offset); + + //-------------------------------------------------------------------------- + // These methods form a specialized interface to linux threads. + // + bool Resume(); + + void Notify(const ProcessMessage &message); + +private: + RegisterContextPOSIX * + GetRegisterContextPOSIX () + { + if (!m_reg_context_sp) + m_reg_context_sp = GetRegisterContext(); +#if 0 + return dynamic_cast<RegisterContextPOSIX*>(m_reg_context_sp.get()); +#endif + return (RegisterContextPOSIX *)m_reg_context_sp.get(); + } + + std::auto_ptr<lldb_private::StackFrame> m_frame_ap; + + lldb::BreakpointSiteSP m_breakpoint; + lldb::StopInfoSP m_stop_info; + + ProcessMonitor & + GetMonitor(); + + lldb::StopInfoSP + GetPrivateStopReason(); + + void BreakNotify(const ProcessMessage &message); + void TraceNotify(const ProcessMessage &message); + void LimboNotify(const ProcessMessage &message); + void SignalNotify(const ProcessMessage &message); + void SignalDeliveredNotify(const ProcessMessage &message); + void CrashNotify(const ProcessMessage &message); + + lldb_private::Unwind * + GetUnwinder(); +}; + +#endif // #ifndef liblldb_POSIXThread_H_ diff --git a/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp b/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp new file mode 100644 index 00000000000..2e72b592a0d --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp @@ -0,0 +1,245 @@ +//===-- ProcessMessage.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessMessage.h" + +using namespace lldb_private; + +const char * +ProcessMessage::GetCrashReasonString(CrashReason reason) +{ + const char *str = NULL; + + switch (reason) + { + default: + assert(false && "invalid CrashReason"); + break; + + case eInvalidAddress: + str = "invalid address"; + break; + case ePrivilegedAddress: + str = "address access protected"; + break; + case eIllegalOpcode: + str = "illegal instruction"; + break; + case eIllegalOperand: + str = "illegal instruction operand"; + break; + case eIllegalAddressingMode: + str = "illegal addressing mode"; + break; + case eIllegalTrap: + str = "illegal trap"; + break; + case ePrivilegedOpcode: + str = "privileged instruction"; + break; + case ePrivilegedRegister: + str = "privileged register"; + break; + case eCoprocessorError: + str = "coprocessor error"; + break; + case eInternalStackError: + str = "internal stack error"; + break; + case eIllegalAlignment: + str = "illegal alignment"; + break; + case eIllegalAddress: + str = "illegal address"; + break; + case eHardwareError: + str = "hardware error"; + break; + case eIntegerDivideByZero: + str = "integer divide by zero"; + break; + case eIntegerOverflow: + str = "integer overflow"; + break; + case eFloatDivideByZero: + str = "floating point divide by zero"; + break; + case eFloatOverflow: + str = "floating point overflow"; + break; + case eFloatUnderflow: + str = "floating point underflow"; + break; + case eFloatInexactResult: + str = "inexact floating point result"; + break; + case eFloatInvalidOperation: + str = "invalid floating point operation"; + break; + case eFloatSubscriptRange: + str = "invalid floating point subscript range"; + break; + } + + return str; +} + +const char * +ProcessMessage::PrintCrashReason(CrashReason reason) +{ +#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION + // Just return the code in asci for integration builds. + chcar str[8]; + sprintf(str, "%d", reason); +#else + const char *str = NULL; + + switch (reason) + { + default: + assert(false && "invalid CrashReason"); + break; + + case eInvalidCrashReason: + str = "eInvalidCrashReason"; + break; + + // SIGSEGV crash reasons. + case eInvalidAddress: + str = "eInvalidAddress"; + break; + case ePrivilegedAddress: + str = "ePrivilegedAddress"; + break; + + // SIGILL crash reasons. + case eIllegalOpcode: + str = "eIllegalOpcode"; + break; + case eIllegalOperand: + str = "eIllegalOperand"; + break; + case eIllegalAddressingMode: + str = "eIllegalAddressingMode"; + break; + case eIllegalTrap: + str = "eIllegalTrap"; + break; + case ePrivilegedOpcode: + str = "ePrivilegedOpcode"; + break; + case ePrivilegedRegister: + str = "ePrivilegedRegister"; + break; + case eCoprocessorError: + str = "eCoprocessorError"; + break; + case eInternalStackError: + str = "eInternalStackError"; + break; + + // SIGBUS crash reasons: + case eIllegalAlignment: + str = "eIllegalAlignment"; + break; + case eIllegalAddress: + str = "eIllegalAddress"; + break; + case eHardwareError: + str = "eHardwareError"; + break; + + // SIGFPE crash reasons: + case eIntegerDivideByZero: + str = "eIntegerDivideByZero"; + break; + case eIntegerOverflow: + str = "eIntegerOverflow"; + break; + case eFloatDivideByZero: + str = "eFloatDivideByZero"; + break; + case eFloatOverflow: + str = "eFloatOverflow"; + break; + case eFloatUnderflow: + str = "eFloatUnderflow"; + break; + case eFloatInexactResult: + str = "eFloatInexactResult"; + break; + case eFloatInvalidOperation: + str = "eFloatInvalidOperation"; + break; + case eFloatSubscriptRange: + str = "eFloatSubscriptRange"; + break; + } +#endif + + return str; +} + +const char * +ProcessMessage::PrintCrashReason() const +{ + return PrintCrashReason(m_crash_reason); +} + +const char * +ProcessMessage::PrintKind(Kind kind) +{ +#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION + // Just return the code in asci for integration builds. + chcar str[8]; + sprintf(str, "%d", reason); +#else + const char *str = NULL; + + switch (kind) + { + default: + assert(false && "invalid Kind"); + break; + + case eInvalidMessage: + str = "eInvalidMessage"; + break; + case eExitMessage: + str = "eExitMessage"; + break; + case eLimboMessage: + str = "eLimboMessage"; + break; + case eSignalMessage: + str = "eSignalMessage"; + break; + case eSignalDeliveredMessage: + str = "eSignalDeliveredMessage"; + break; + case eTraceMessage: + str = "eTraceMessage"; + break; + case eBreakpointMessage: + str = "eBreakpointMessage"; + break; + case eCrashMessage: + str = "eCrashMessage"; + break; + } +#endif + + return str; +} + +const char * +ProcessMessage::PrintKind() const +{ + return PrintKind(m_kind); +} diff --git a/lldb/source/Plugins/Process/POSIX/ProcessMessage.h b/lldb/source/Plugins/Process/POSIX/ProcessMessage.h new file mode 100644 index 00000000000..826567e582b --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/ProcessMessage.h @@ -0,0 +1,171 @@ +//===-- ProcessMessage.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_ProcessMessage_H_ +#define liblldb_ProcessMessage_H_ + +#include <cassert> + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +class ProcessMessage +{ +public: + + /// The type of signal this message can correspond to. + enum Kind + { + eInvalidMessage, + eExitMessage, + eLimboMessage, + eSignalMessage, + eSignalDeliveredMessage, + eTraceMessage, + eBreakpointMessage, + eCrashMessage + }; + + enum CrashReason + { + eInvalidCrashReason, + + // SIGSEGV crash reasons. + eInvalidAddress, + ePrivilegedAddress, + + // SIGILL crash reasons. + eIllegalOpcode, + eIllegalOperand, + eIllegalAddressingMode, + eIllegalTrap, + ePrivilegedOpcode, + ePrivilegedRegister, + eCoprocessorError, + eInternalStackError, + + // SIGBUS crash reasons, + eIllegalAlignment, + eIllegalAddress, + eHardwareError, + + // SIGFPE crash reasons, + eIntegerDivideByZero, + eIntegerOverflow, + eFloatDivideByZero, + eFloatOverflow, + eFloatUnderflow, + eFloatInexactResult, + eFloatInvalidOperation, + eFloatSubscriptRange + }; + + ProcessMessage() + : m_tid(LLDB_INVALID_PROCESS_ID), + m_kind(eInvalidMessage), + m_crash_reason(eInvalidCrashReason), + m_status(0), + m_addr(0) { } + + Kind GetKind() const { return m_kind; } + + lldb::tid_t GetTID() const { return m_tid; } + + /// Indicates that the thread @p tid is about to exit with status @p status. + static ProcessMessage Limbo(lldb::tid_t tid, int status) { + return ProcessMessage(tid, eLimboMessage, status); + } + + /// Indicates that the thread @p tid had the signal @p signum delivered. + static ProcessMessage Signal(lldb::tid_t tid, int signum) { + return ProcessMessage(tid, eSignalMessage, signum); + } + + /// Indicates that a signal @p signum generated by the debugging process was + /// delivered to the thread @p tid. + static ProcessMessage SignalDelivered(lldb::tid_t tid, int signum) { + return ProcessMessage(tid, eSignalDeliveredMessage, signum); + } + + /// Indicates that the thread @p tid encountered a trace point. + static ProcessMessage Trace(lldb::tid_t tid) { + return ProcessMessage(tid, eTraceMessage); + } + + /// Indicates that the thread @p tid encountered a break point. + static ProcessMessage Break(lldb::tid_t tid) { + return ProcessMessage(tid, eBreakpointMessage); + } + + /// Indicates that the thread @p tid crashed. + static ProcessMessage Crash(lldb::pid_t pid, CrashReason reason, + int signo, lldb::addr_t fault_addr) { + ProcessMessage message(pid, eCrashMessage, signo, fault_addr); + message.m_crash_reason = reason; + return message; + } + + int GetExitStatus() const { + assert(GetKind() == eExitMessage || GetKind() == eLimboMessage); + return m_status; + } + + int GetSignal() const { + assert(GetKind() == eSignalMessage || GetKind() == eCrashMessage || + GetKind() == eSignalDeliveredMessage); + return m_status; + } + + int GetStopStatus() const { + assert(GetKind() == eSignalMessage); + return m_status; + } + + CrashReason GetCrashReason() const { + assert(GetKind() == eCrashMessage); + return m_crash_reason; + } + + lldb::addr_t GetFaultAddress() const { + assert(GetKind() == eCrashMessage); + return m_addr; + } + + static const char * + GetCrashReasonString(CrashReason reason); + + const char * + PrintCrashReason() const; + + static const char * + PrintCrashReason(CrashReason reason); + + const char * + PrintKind() const; + + static const char * + PrintKind(Kind); + +private: + ProcessMessage(lldb::tid_t tid, Kind kind, + int status = 0, lldb::addr_t addr = 0) + : m_tid(tid), + m_kind(kind), + m_crash_reason(eInvalidCrashReason), + m_status(status), + m_addr(addr) { } + + lldb::tid_t m_tid; + Kind m_kind : 8; + CrashReason m_crash_reason : 8; + int m_status; + lldb::addr_t m_addr; +}; + +#endif // #ifndef liblldb_ProcessMessage_H_ diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp new file mode 100644 index 00000000000..67cf8a9311b --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp @@ -0,0 +1,594 @@ +//===-- ProcessPOSIX.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 <errno.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Target.h" + +#include "ProcessPOSIX.h" +#include "ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/InferiorCallPOSIX.h" +#include "ProcessMonitor.h" +#include "POSIXThread.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------------ +// Static functions. +#if 0 +Process* +ProcessPOSIX::CreateInstance(Target& target, Listener &listener) +{ + return new ProcessPOSIX(target, listener); +} + + +void +ProcessPOSIX::Initialize() +{ + static bool g_initialized = false; + + if (!g_initialized) + { + g_initialized = true; + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + + Log::Callbacks log_callbacks = { + ProcessPOSIXLog::DisableLog, + ProcessPOSIXLog::EnableLog, + ProcessPOSIXLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessPOSIX::GetPluginNameStatic(), log_callbacks); + } +} +#endif + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessPOSIX::ProcessPOSIX(Target& target, Listener &listener) + : Process(target, listener), + m_monitor(NULL), + m_module(NULL), + m_in_limbo(false), + m_exit_now(false) +{ + // FIXME: Putting this code in the ctor and saving the byte order in a + // member variable is a hack to avoid const qual issues in GetByteOrder. + ObjectFile *obj_file = GetTarget().GetExecutableModule()->GetObjectFile(); + m_byte_order = obj_file->GetByteOrder(); +} + +ProcessPOSIX::~ProcessPOSIX() +{ + delete m_monitor; +} + +//------------------------------------------------------------------------------ +// Process protocol. + +bool +ProcessPOSIX::CanDebug(Target &target, bool plugin_specified_by_name) +{ + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target.GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + return false; +} + +Error +ProcessPOSIX::DoAttachToProcessWithID(lldb::pid_t pid) +{ + Error error; + assert(m_monitor == NULL); + + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("ProcessPOSIX::%s(pid = %i)", __FUNCTION__, GetID()); + + m_monitor = new ProcessMonitor(this, pid, error); + + if (!error.Success()) + return error; + + SetID(pid); + return error; +} + +Error +ProcessPOSIX::WillLaunch(Module* module) +{ + Error error; + return error; +} + +const char * +ProcessPOSIX::GetFilePath( + const lldb_private::ProcessLaunchInfo::FileAction *file_action, + const char *default_path) +{ + const char *pts_name = "/dev/pts/"; + const char *path = NULL; + + if (file_action) + { + if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen) + path = file_action->GetPath(); + // By default the stdio paths passed in will be pseudo-terminal + // (/dev/pts). If so, convert to using a different default path + // instead to redirect I/O to the debugger console. This should + // also handle user overrides to /dev/null or a different file. + if (::strncmp(path, pts_name, ::strlen(pts_name)) == 0) + path = default_path; + } + + return path; +} + +Error +ProcessPOSIX::DoLaunch (Module *module, + const ProcessLaunchInfo &launch_info) +{ + Error error; + assert(m_monitor == NULL); + + SetPrivateState(eStateLaunching); + + const lldb_private::ProcessLaunchInfo::FileAction *file_action; + + // Default of NULL will mean to use existing open file descriptors + const char *stdin_path = NULL; + const char *stdout_path = NULL; + const char *stderr_path = NULL; + + file_action = launch_info.GetFileActionForFD (STDIN_FILENO); + stdin_path = GetFilePath(file_action, stdin_path); + + file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); + stdout_path = GetFilePath(file_action, stdout_path); + + file_action = launch_info.GetFileActionForFD (STDERR_FILENO); + stderr_path = GetFilePath(file_action, stderr_path); + + m_monitor = new ProcessMonitor (this, + module, + launch_info.GetArguments().GetConstArgumentVector(), + launch_info.GetEnvironmentEntries().GetConstArgumentVector(), + stdin_path, + stdout_path, + stderr_path, + error); + + m_module = module; + + if (!error.Success()) + return error; + + SetID(m_monitor->GetPID()); + return error; +} + +void +ProcessPOSIX::DidLaunch() +{ +} + +Error +ProcessPOSIX::DoResume() +{ + StateType state = GetPrivateState(); + + assert(state == eStateStopped || state == eStateCrashed); + + // We are about to resume a thread that will cause the process to exit so + // set our exit status now. Do not change our state if the inferior + // crashed. + if (state == eStateStopped) + { + if (m_in_limbo) + SetExitStatus(m_exit_status, NULL); + else + SetPrivateState(eStateRunning); + } + + bool did_resume = false; + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(i, false).get()); + did_resume = thread->Resume() || did_resume; + } + assert(did_resume && "Process resume failed!"); + + return Error(); +} + +addr_t +ProcessPOSIX::GetImageInfoAddress() +{ + Target *target = &GetTarget(); + ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(); + + if (addr.IsValid()) + return addr.GetLoadAddress(target); + else + return LLDB_INVALID_ADDRESS; +} + +Error +ProcessPOSIX::DoHalt(bool &caused_stop) +{ + Error error; + + if (IsStopped()) + { + caused_stop = false; + } + else if (kill(GetID(), SIGSTOP)) + { + caused_stop = false; + error.SetErrorToErrno(); + } + else + { + caused_stop = true; + } + + return error; +} + +Error +ProcessPOSIX::DoDetach() +{ + Error error; + + error = m_monitor->Detach(); + if (error.Success()) + SetPrivateState(eStateDetached); + + return error; +} + +Error +ProcessPOSIX::DoSignal(int signal) +{ + Error error; + + if (kill(GetID(), signal)) + error.SetErrorToErrno(); + + return error; +} + +Error +ProcessPOSIX::DoDestroy() +{ + Error error; + + if (!HasExited()) + { + // Drive the exit event to completion (do not keep the inferior in + // limbo). + m_exit_now = true; + + if (kill(m_monitor->GetPID(), SIGKILL) && error.Success()) + { + error.SetErrorToErrno(); + return error; + } + + SetPrivateState(eStateExited); + } + + return error; +} + +void +ProcessPOSIX::SendMessage(const ProcessMessage &message) +{ + Mutex::Locker lock(m_message_mutex); + + switch (message.GetKind()) + { + default: + assert(false && "Unexpected process message!"); + break; + + case ProcessMessage::eInvalidMessage: + return; + + case ProcessMessage::eLimboMessage: + m_in_limbo = true; + m_exit_status = message.GetExitStatus(); + if (m_exit_now) + { + SetPrivateState(eStateExited); + m_monitor->Detach(); + } + else + SetPrivateState(eStateStopped); + break; + + case ProcessMessage::eExitMessage: + m_exit_status = message.GetExitStatus(); + SetExitStatus(m_exit_status, NULL); + break; + + case ProcessMessage::eTraceMessage: + case ProcessMessage::eBreakpointMessage: + SetPrivateState(eStateStopped); + break; + + case ProcessMessage::eSignalMessage: + case ProcessMessage::eSignalDeliveredMessage: + SetPrivateState(eStateStopped); + break; + + case ProcessMessage::eCrashMessage: + SetPrivateState(eStateCrashed); + break; + } + + m_message_queue.push(message); +} + +void +ProcessPOSIX::RefreshStateAfterStop() +{ + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("ProcessPOSIX::%s()", __FUNCTION__); + + Mutex::Locker lock(m_message_mutex); + if (m_message_queue.empty()) + return; + + ProcessMessage &message = m_message_queue.front(); + + // Resolve the thread this message corresponds to and pass it along. + // FIXME: we're really dealing with the pid here. This should get + // fixed when this code is fixed to handle multiple threads. + lldb::tid_t tid = message.GetTID(); + if (log) + log->Printf ("ProcessPOSIX::%s() pid = %i", __FUNCTION__, tid); + POSIXThread *thread = static_cast<POSIXThread*>( + GetThreadList().FindThreadByID(tid, false).get()); + + assert(thread); + thread->Notify(message); + + m_message_queue.pop(); +} + +bool +ProcessPOSIX::IsAlive() +{ + StateType state = GetPrivateState(); + return state != eStateDetached && state != eStateExited && state != eStateInvalid; +} + +size_t +ProcessPOSIX::DoReadMemory(addr_t vm_addr, + void *buf, size_t size, Error &error) +{ + assert(m_monitor); + return m_monitor->ReadMemory(vm_addr, buf, size, error); +} + +size_t +ProcessPOSIX::DoWriteMemory(addr_t vm_addr, const void *buf, size_t size, + Error &error) +{ + assert(m_monitor); + return m_monitor->WriteMemory(vm_addr, buf, size, error); +} + +addr_t +ProcessPOSIX::DoAllocateMemory(size_t size, uint32_t permissions, + Error &error) +{ + addr_t allocated_addr = LLDB_INVALID_ADDRESS; + + unsigned prot = 0; + if (permissions & lldb::ePermissionsReadable) + prot |= eMmapProtRead; + if (permissions & lldb::ePermissionsWritable) + prot |= eMmapProtWrite; + if (permissions & lldb::ePermissionsExecutable) + prot |= eMmapProtExec; + + if (InferiorCallMmap(this, allocated_addr, 0, size, prot, + eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) { + m_addr_to_mmap_size[allocated_addr] = size; + error.Clear(); + } else { + allocated_addr = LLDB_INVALID_ADDRESS; + error.SetErrorStringWithFormat("unable to allocate %zu bytes of memory with permissions %s", size, GetPermissionsAsCString (permissions)); + } + + return allocated_addr; +} + +Error +ProcessPOSIX::DoDeallocateMemory(lldb::addr_t addr) +{ + Error error; + MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); + if (pos != m_addr_to_mmap_size.end() && + InferiorCallMunmap(this, addr, pos->second)) + m_addr_to_mmap_size.erase (pos); + else + error.SetErrorStringWithFormat("unable to deallocate memory at 0x%llx", addr); + + return error; +} + +size_t +ProcessPOSIX::GetSoftwareBreakpointTrapOpcode(BreakpointSite* bp_site) +{ + static const uint8_t g_i386_opcode[] = { 0xCC }; + + ArchSpec arch = GetTarget().GetArchitecture(); + const uint8_t *opcode = NULL; + size_t opcode_size = 0; + + switch (arch.GetCore()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_64_x86_64: + opcode = g_i386_opcode; + opcode_size = sizeof(g_i386_opcode); + break; + } + + bp_site->SetTrapOpcode(opcode, opcode_size); + return opcode_size; +} + +Error +ProcessPOSIX::EnableBreakpoint(BreakpointSite *bp_site) +{ + return EnableSoftwareBreakpoint(bp_site); +} + +Error +ProcessPOSIX::DisableBreakpoint(BreakpointSite *bp_site) +{ + return DisableSoftwareBreakpoint(bp_site); +} + +uint32_t +ProcessPOSIX::UpdateThreadListIfNeeded() +{ + // Do not allow recursive updates. + return m_thread_list.GetSize(false); +} + +uint32_t +ProcessPOSIX::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("ProcessPOSIX::%s() (pid = %i)", __FUNCTION__, GetID()); + + // Update the process thread list with this new thread. + // FIXME: We should be using tid, not pid. + assert(m_monitor); + ThreadSP thread_sp (old_thread_list.FindThreadByID (GetID(), false)); + if (!thread_sp) + thread_sp.reset(new POSIXThread(*this, GetID())); + + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("ProcessPOSIX::%s() updated pid = %i", __FUNCTION__, GetID()); + new_thread_list.AddThread(thread_sp); + + return new_thread_list.GetSize(false); +} + +ByteOrder +ProcessPOSIX::GetByteOrder() const +{ + // FIXME: We should be able to extract this value directly. See comment in + // ProcessPOSIX(). + return m_byte_order; +} + +size_t +ProcessPOSIX::PutSTDIN(const char *buf, size_t len, Error &error) +{ + ssize_t status; + if ((status = write(m_monitor->GetTerminalFD(), buf, len)) < 0) + { + error.SetErrorToErrno(); + return 0; + } + return status; +} + +size_t +ProcessPOSIX::GetSTDOUT(char *buf, size_t len, Error &error) +{ + ssize_t bytes_read; + + // The terminal file descriptor is always in non-block mode. + if ((bytes_read = read(m_monitor->GetTerminalFD(), buf, len)) < 0) + { + if (errno != EAGAIN) + error.SetErrorToErrno(); + return 0; + } + return bytes_read; +} + +size_t +ProcessPOSIX::GetSTDERR(char *buf, size_t len, Error &error) +{ + return GetSTDOUT(buf, len, error); +} + +UnixSignals & +ProcessPOSIX::GetUnixSignals() +{ + return m_signals; +} + +//------------------------------------------------------------------------------ +// Utility functions. + +bool +ProcessPOSIX::HasExited() +{ + switch (GetPrivateState()) + { + default: + break; + + case eStateDetached: + case eStateExited: + return true; + } + + return false; +} + +bool +ProcessPOSIX::IsStopped() +{ + switch (GetPrivateState()) + { + default: + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + return true; + } + + return false; +} diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h new file mode 100644 index 00000000000..dd887cf0894 --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h @@ -0,0 +1,180 @@ +//===-- ProcessPOSIX.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_ProcessPOSIX_H_ +#define liblldb_ProcessPOSIX_H_ + +// C Includes + +// C++ Includes +#include <queue> + +// Other libraries and framework includes +#include "lldb/Target/Process.h" +#include "lldb/Target/UnixSignals.h" +#include "ProcessMessage.h" + +class ProcessMonitor; + +class ProcessPOSIX : + public lldb_private::Process +{ +public: + + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessPOSIX(lldb_private::Target& target, + lldb_private::Listener &listener); + + virtual + ~ProcessPOSIX(); + + //------------------------------------------------------------------ + // Process protocol. + //------------------------------------------------------------------ + virtual bool + CanDebug(lldb_private::Target &target, bool plugin_specified_by_name); + + virtual lldb_private::Error + WillLaunch(lldb_private::Module *module); + + virtual lldb_private::Error + DoAttachToProcessWithID(lldb::pid_t pid); + + virtual lldb_private::Error + DoLaunch (lldb_private::Module *exe_module, + const lldb_private::ProcessLaunchInfo &launch_info); + + virtual void + DidLaunch(); + + virtual lldb_private::Error + DoResume(); + + virtual lldb_private::Error + DoHalt(bool &caused_stop); + + virtual lldb_private::Error + DoDetach(); + + virtual lldb_private::Error + DoSignal(int signal); + + virtual lldb_private::Error + DoDestroy(); + + virtual void + RefreshStateAfterStop(); + + virtual bool + IsAlive(); + + virtual size_t + DoReadMemory(lldb::addr_t vm_addr, + void *buf, + size_t size, + lldb_private::Error &error); + + virtual size_t + DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, + lldb_private::Error &error); + + virtual lldb::addr_t + DoAllocateMemory(size_t size, uint32_t permissions, + lldb_private::Error &error); + + virtual lldb_private::Error + DoDeallocateMemory(lldb::addr_t ptr); + + virtual size_t + GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite* bp_site); + + virtual lldb_private::Error + EnableBreakpoint(lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpoint(lldb_private::BreakpointSite *bp_site); + + virtual uint32_t + UpdateThreadListIfNeeded(); + + virtual uint32_t + UpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) = 0; + + virtual lldb::ByteOrder + GetByteOrder() const; + + virtual lldb::addr_t + GetImageInfoAddress(); + + virtual size_t + PutSTDIN(const char *buf, size_t len, lldb_private::Error &error); + + virtual size_t + GetSTDOUT(char *buf, size_t len, lldb_private::Error &error); + + virtual size_t + GetSTDERR(char *buf, size_t len, lldb_private::Error &error); + + //-------------------------------------------------------------------------- + // ProcessPOSIX internal API. + + /// Registers the given message with this process. + void SendMessage(const ProcessMessage &message); + + ProcessMonitor & + GetMonitor() { assert(m_monitor); return *m_monitor; } + + lldb_private::UnixSignals & + GetUnixSignals(); + + const char * + GetFilePath(const lldb_private::ProcessLaunchInfo::FileAction *file_action, + const char *default_path); + +protected: + /// Target byte order. + lldb::ByteOrder m_byte_order; + + /// Process monitor; + ProcessMonitor *m_monitor; + + /// The module we are executing. + lldb_private::Module *m_module; + + /// Message queue notifying this instance of inferior process state changes. + lldb_private::Mutex m_message_mutex; + std::queue<ProcessMessage> m_message_queue; + + /// True when the process has entered a state of "limbo". + /// + /// This flag qualifies eStateStopped. It lets us know that when we + /// continue from this state the process will exit. Also, when true, + /// Process::m_exit_status is set. + bool m_in_limbo; + + /// Drive any exit events to completion. + bool m_exit_now; + + /// OS-specific signal set. + lldb_private::UnixSignals m_signals; + + /// Returns true if the process has exited. + bool HasExited(); + + /// Returns true if the process is stopped. + bool IsStopped(); + + typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap; + MMapMap m_addr_to_mmap_size; +}; + +#endif // liblldb_MacOSXProcess_H_ diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp b/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp new file mode 100644 index 00000000000..6e7ae1464b4 --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp @@ -0,0 +1,195 @@ +//===-- ProcessPOSIXLog.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessPOSIXLog.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/StreamFile.h" + +#include "ProcessPOSIX.h" +#include "ProcessPOSIXLog.h" + +using namespace lldb; +using namespace lldb_private; + + +// We want to avoid global constructors where code needs to be run so here we +// control access to our static g_log_sp by hiding it in a singleton function +// that will construct the static g_lob_sp the first time this function is +// called. +static LogSP & +GetLog () +{ + static LogSP g_log_sp; + return g_log_sp; +} + +LogSP +ProcessPOSIXLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + LogSP log(GetLog ()); + if (log && mask) + { + uint32_t log_mask = log->GetMask().Get(); + if ((log_mask & mask) != mask) + return LogSP(); + } + return log; +} + +void +ProcessPOSIXLog::DisableLog (Args &args, Stream *feedback_strm) +{ + LogSP log (GetLog ()); + if (log) + { + uint32_t flag_bits = 0; + + const size_t argc = args.GetArgumentCount (); + if (argc > 0) + { + flag_bits = log->GetMask().Get(); + for (size_t i = 0; i < argc; ++i) + { + const char *arg = args.GetArgumentAtIndex (i); + + + if (::strcasecmp (arg, "all") == 0 ) flag_bits &= ~POSIX_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) flag_bits &= ~POSIX_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits &= ~POSIX_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits &= ~POSIX_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits &= ~POSIX_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits &= ~POSIX_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits &= ~POSIX_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits &= ~POSIX_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits &= ~POSIX_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits &= ~POSIX_LOG_PROCESS; + else if (::strcasecmp (arg, "ptrace") == 0 ) flag_bits &= ~POSIX_LOG_PTRACE; + else if (::strcasecmp (arg, "registers") == 0 ) flag_bits &= ~POSIX_LOG_REGISTERS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits &= ~POSIX_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits &= ~POSIX_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits &= ~POSIX_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits &= ~POSIX_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListLogCategories (feedback_strm); + } + + } + } + + if (flag_bits == 0) + GetLog ().reset(); + else + log->GetMask().Reset (flag_bits); + } + + return; +} + +LogSP +ProcessPOSIXLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &args, Stream *feedback_strm) +{ + // Try see if there already is a log - that way we can reuse its settings. + // We could reuse the log in toto, but we don't know that the stream is the same. + uint32_t flag_bits = 0; + LogSP log(GetLog ()); + if (log) + flag_bits = log->GetMask().Get(); + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + log = make_shared<Log>(log_stream_sp); + GetLog () = log; + } + + if (log) + { + bool got_unknown_category = false; + const size_t argc = args.GetArgumentCount(); + for (size_t i=0; i<argc; ++i) + { + const char *arg = args.GetArgumentAtIndex(i); + + if (::strcasecmp (arg, "all") == 0 ) flag_bits |= POSIX_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) flag_bits |= POSIX_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits |= POSIX_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits |= POSIX_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= POSIX_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits |= POSIX_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits |= POSIX_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits |= POSIX_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits |= POSIX_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits |= POSIX_LOG_PROCESS; + else if (::strcasecmp (arg, "ptrace") == 0 ) flag_bits |= POSIX_LOG_PTRACE; + else if (::strcasecmp (arg, "registers") == 0 ) flag_bits |= POSIX_LOG_REGISTERS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits |= POSIX_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits |= POSIX_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits |= POSIX_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits |= POSIX_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = POSIX_LOG_DEFAULT; + log->GetMask().Reset(flag_bits); + log->GetOptions().Reset(log_options); + } + return log; +} + +void +ProcessPOSIXLog::ListLogCategories (Stream *strm) +{ + strm->Printf ("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " async - log asynchronous activity\n" + " break - log breakpoints\n" + " communication - log communication activity\n" + " default - enable the default set of logging categories for liblldb\n" + " packets - log gdb remote packets\n" + " memory - log memory reads and writes\n" + " data-short - log memory bytes for memory reads and writes for short transactions only\n" + " data-long - log memory bytes for memory reads and writes for all transactions\n" + " process - log process events and activities\n" +#ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION + " ptrace - log all calls to ptrace\n" +#endif + " registers - log register read/writes\n" + " thread - log thread events and activities\n" + " step - log step related activities\n" + " verbose - enable verbose logging\n" + " watch - log watchpoint related activities\n", ProcessPOSIXLog::m_pluginname); +} + + +void +ProcessPOSIXLog::LogIf (uint32_t mask, const char *format, ...) +{ + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (mask)); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} + +int ProcessPOSIXLog::m_nestinglevel; +const char *ProcessPOSIXLog::m_pluginname = ""; diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h b/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h new file mode 100644 index 00000000000..5c34a88af7c --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h @@ -0,0 +1,106 @@ +//===-- ProcessLinuxLog.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_ProcessLinuxLog_h_ +#define liblldb_ProcessLinuxLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define POSIX_LOG_VERBOSE (1u << 0) +#define POSIX_LOG_PROCESS (1u << 1) +#define POSIX_LOG_THREAD (1u << 2) +#define POSIX_LOG_PACKETS (1u << 3) +#define POSIX_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define POSIX_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes +#define POSIX_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define POSIX_LOG_BREAKPOINTS (1u << 7) +#define POSIX_LOG_WATCHPOINTS (1u << 8) +#define POSIX_LOG_STEP (1u << 9) +#define POSIX_LOG_COMM (1u << 10) +#define POSIX_LOG_ASYNC (1u << 11) +#define POSIX_LOG_PTRACE (1u << 12) +#define POSIX_LOG_REGISTERS (1u << 13) +#define POSIX_LOG_ALL (UINT32_MAX) +#define POSIX_LOG_DEFAULT POSIX_LOG_PACKETS + +// The size which determines "short memory reads/writes". +#define POSIX_LOG_MEMORY_SHORT_BYTES (4 * sizeof(ptrdiff_t)) + +class ProcessPOSIXLog +{ + static int m_nestinglevel; + static const char *m_pluginname; + +public: + static void + RegisterPluginName(const char *pluginName) + { + m_pluginname = pluginName; + } + + + static lldb::LogSP + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static void + DisableLog (lldb_private::Args &args, lldb_private::Stream *feedback_strm); + + static lldb::LogSP + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, + lldb_private::Args &args, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories (lldb_private::Stream *strm); + + static void + LogIf (uint32_t mask, const char *format, ...); + + // The following functions can be used to enable the client to limit + // logging to only the top level function calls. This is useful for + // recursive functions. FIXME: not thread safe! + // Example: + // void NestingFunc() { + // LogSP log (ProcessLinuxLog::GetLogIfAllCategoriesSet(POSIX_LOG_ALL)); + // if (log) + // { + // ProcessLinuxLog::IncNestLevel(); + // if (ProcessLinuxLog::AtTopNestLevel()) + // log->Print(msg); + // } + // NestingFunc(); + // if (log) + // ProcessLinuxLog::DecNestLevel(); + // } + + static bool + AtTopNestLevel() + { + return m_nestinglevel == 1; + } + + static void + IncNestLevel() + { + ++m_nestinglevel; + } + + static void + DecNestLevel() + { + --m_nestinglevel; + assert(m_nestinglevel >= 0); + } +}; + +#endif // liblldb_ProcessLinuxLog_h_ diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h b/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h new file mode 100644 index 00000000000..f97e710fcaa --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h @@ -0,0 +1,40 @@ +//===-- RegisterContextPOSIX.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_RegisterContextPOSIX_H_ +#define liblldb_RegisterContextPOSIX_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Target/RegisterContext.h" + +//------------------------------------------------------------------------------ +/// @class RegisterContextPOSIX +/// +/// @brief Extends RegisterClass with a few virtual operations useful on POSIX. +class RegisterContextPOSIX + : public lldb_private::RegisterContext +{ +public: + RegisterContextPOSIX(lldb_private::Thread &thread, + uint32_t concrete_frame_idx) + : RegisterContext(thread, concrete_frame_idx) { } + + /// Updates the register state of the associated thread after hitting a + /// breakpoint (if that make sense for the architecture). Default + /// implementation simply returns true for architectures which do not + /// require any update. + /// + /// @return + /// True if the operation succeeded and false otherwise. + virtual bool UpdateAfterBreakpoint() { return true; } +}; + +#endif // #ifndef liblldb_RegisterContextPOSIX_H_ diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContext_i386.cpp b/lldb/source/Plugins/Process/POSIX/RegisterContext_i386.cpp new file mode 100644 index 00000000000..9aef2375928 --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/RegisterContext_i386.cpp @@ -0,0 +1,643 @@ +//===-- RegisterContextPOSIX_i386.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/Core/DataExtractor.h" +#include "lldb/Target/Thread.h" +#include "lldb/Host/Endian.h" + +#include "ProcessPOSIX.h" +#include "ProcessPOSIXLog.h" +#include "ProcessMonitor.h" +#include "RegisterContext_i386.h" + +using namespace lldb_private; +using namespace lldb; + +enum +{ + k_first_gpr, + gpr_eax = k_first_gpr, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, +#ifdef __FreeBSD__ + gpr_orig_ax, +#endif + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs, + k_last_gpr = gpr_gs, + + k_first_fpr, + fpu_fcw = k_first_fpr, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_foo, + fpu_fos, + fpu_mxcsr, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + k_last_fpr = fpu_xmm7, + + k_num_registers, + k_num_gpr_registers = k_last_gpr - k_first_gpr + 1, + k_num_fpu_registers = k_last_fpr - k_first_fpr + 1 +}; + +// Number of register sets provided by this context. +enum +{ + k_num_register_sets = 2 +}; + +enum +{ + gcc_eax = 0, + gcc_ecx, + gcc_edx, + gcc_ebx, + gcc_ebp, + gcc_esp, + gcc_esi, + gcc_edi, + gcc_eip, + gcc_eflags +}; + +enum +{ + dwarf_eax = 0, + dwarf_ecx, + dwarf_edx, + dwarf_ebx, + dwarf_esp, + dwarf_ebp, + dwarf_esi, + dwarf_edi, + dwarf_eip, + dwarf_eflags, + dwarf_stmm0 = 11, + dwarf_stmm1, + dwarf_stmm2, + dwarf_stmm3, + dwarf_stmm4, + dwarf_stmm5, + dwarf_stmm6, + dwarf_stmm7, + dwarf_xmm0 = 21, + dwarf_xmm1, + dwarf_xmm2, + dwarf_xmm3, + dwarf_xmm4, + dwarf_xmm5, + dwarf_xmm6, + dwarf_xmm7 +}; + +enum +{ + gdb_eax = 0, + gdb_ecx = 1, + gdb_edx = 2, + gdb_ebx = 3, + gdb_esp = 4, + gdb_ebp = 5, + gdb_esi = 6, + gdb_edi = 7, + gdb_eip = 8, + gdb_eflags = 9, + gdb_cs = 10, + gdb_ss = 11, + gdb_ds = 12, + gdb_es = 13, + gdb_fs = 14, + gdb_gs = 15, + gdb_stmm0 = 16, + gdb_stmm1 = 17, + gdb_stmm2 = 18, + gdb_stmm3 = 19, + gdb_stmm4 = 20, + gdb_stmm5 = 21, + gdb_stmm6 = 22, + gdb_stmm7 = 23, + gdb_fcw = 24, + gdb_fsw = 25, + gdb_ftw = 26, + gdb_fpu_cs = 27, + gdb_ip = 28, + gdb_fpu_ds = 29, + gdb_dp = 30, + gdb_fop = 31, + gdb_xmm0 = 32, + gdb_xmm1 = 33, + gdb_xmm2 = 34, + gdb_xmm3 = 35, + gdb_xmm4 = 36, + gdb_xmm5 = 37, + gdb_xmm6 = 38, + gdb_xmm7 = 39, + gdb_mxcsr = 40, + gdb_mm0 = 41, + gdb_mm1 = 42, + gdb_mm2 = 43, + gdb_mm3 = 44, + gdb_mm4 = 45, + gdb_mm5 = 46, + gdb_mm6 = 47, + gdb_mm7 = 48 +}; + +static const +uint32_t g_gpr_regnums[k_num_gpr_registers] = +{ + gpr_eax, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, +#ifdef __FreeBSD__ + gpr_orig_ax, +#endif + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs, +}; + +static const uint32_t +g_fpu_regnums[k_num_fpu_registers] = +{ + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_foo, + fpu_fos, + fpu_mxcsr, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, +}; + +static const RegisterSet +g_reg_sets[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums } +}; + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) \ + (offsetof(RegisterContext_i386::UserArea, regs) + \ + offsetof(RegisterContext_i386::GPR, regname)) + +// Computes the offset of the given FPR in the user data area. +#define FPR_OFFSET(regname) \ + (offsetof(RegisterContext_i386::UserArea, i387) + \ + offsetof(RegisterContext_i386::FPU, regname)) + +// Number of bytes needed to represent a GPR. +#define GPR_SIZE(reg) sizeof(((RegisterContext_i386::GPR*)NULL)->reg) + +// Number of bytes needed to represent a FPR. +#define FPR_SIZE(reg) sizeof(((RegisterContext_i386::FPU*)NULL)->reg) + +// Number of bytes needed to represent the i'th FP register. +#define FP_SIZE sizeof(((RegisterContext_i386::MMSReg*)NULL)->bytes) + +// Number of bytes needed to represent an XMM register. +#define XMM_SIZE sizeof(RegisterContext_i386::XMMReg) + +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg } } + +#define DEFINE_FPR(reg, kind1, kind2, kind3, kind4) \ + { #reg, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, fpu_##reg } } + +#define DEFINE_FP(reg, i) \ + { #reg#i, NULL, FP_SIZE, FPR_OFFSET(reg[i]), eEncodingVector, \ + eFormatVectorOfUInt8, \ + { dwarf_##reg##i, dwarf_##reg##i, \ + LLDB_INVALID_REGNUM, gdb_##reg##i, fpu_##reg##i } } + +#define DEFINE_XMM(reg, i) \ + { #reg#i, NULL, XMM_SIZE, FPR_OFFSET(reg[i]), eEncodingVector, \ + eFormatVectorOfUInt8, \ + { dwarf_##reg##i, dwarf_##reg##i, \ + LLDB_INVALID_REGNUM, gdb_##reg##i, fpu_##reg##i } } + +static RegisterInfo +g_register_infos[k_num_registers] = +{ + // General purpose registers. + DEFINE_GPR(eax, NULL, gcc_eax, dwarf_eax, LLDB_INVALID_REGNUM, gdb_eax), + DEFINE_GPR(ebx, NULL, gcc_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, gdb_ebx), + DEFINE_GPR(ecx, NULL, gcc_ecx, dwarf_ecx, LLDB_INVALID_REGNUM, gdb_ecx), + DEFINE_GPR(edx, NULL, gcc_edx, dwarf_edx, LLDB_INVALID_REGNUM, gdb_edx), + DEFINE_GPR(edi, NULL, gcc_edi, dwarf_edi, LLDB_INVALID_REGNUM, gdb_edi), + DEFINE_GPR(esi, NULL, gcc_esi, dwarf_esi, LLDB_INVALID_REGNUM, gdb_esi), + DEFINE_GPR(ebp, "fp", gcc_ebp, dwarf_ebp, LLDB_INVALID_REGNUM, gdb_ebp), + DEFINE_GPR(esp, "sp", gcc_esp, dwarf_esp, LLDB_INVALID_REGNUM, gdb_esp), + DEFINE_GPR(ss, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ss), + DEFINE_GPR(eflags, "flags", gcc_eflags, dwarf_eflags, LLDB_INVALID_REGNUM, gdb_eflags), + DEFINE_GPR(eip, "pc", gcc_eip, dwarf_eip, LLDB_INVALID_REGNUM, gdb_eip), + DEFINE_GPR(cs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_cs), + DEFINE_GPR(ds, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ds), + DEFINE_GPR(es, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_es), + DEFINE_GPR(fs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fs), + DEFINE_GPR(gs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gs), + + // Floating point registers. + DEFINE_FPR(fcw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fcw), + DEFINE_FPR(fsw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fsw), + DEFINE_FPR(ftw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ftw), + DEFINE_FPR(fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fop), + DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ip), + DEFINE_FPR(cs, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_cs), + DEFINE_FPR(foo, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_dp), + DEFINE_FPR(fos, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ds), + DEFINE_FPR(mxcsr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_mxcsr), + + DEFINE_FP(stmm, 0), + DEFINE_FP(stmm, 1), + DEFINE_FP(stmm, 2), + DEFINE_FP(stmm, 3), + DEFINE_FP(stmm, 4), + DEFINE_FP(stmm, 5), + DEFINE_FP(stmm, 6), + DEFINE_FP(stmm, 7), + + // XMM registers + DEFINE_XMM(xmm, 0), + DEFINE_XMM(xmm, 1), + DEFINE_XMM(xmm, 2), + DEFINE_XMM(xmm, 3), + DEFINE_XMM(xmm, 4), + DEFINE_XMM(xmm, 5), + DEFINE_XMM(xmm, 6), + DEFINE_XMM(xmm, 7), + +}; + +#ifndef NDEBUG +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); +#endif + +static unsigned GetRegOffset(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register number."); + return g_register_infos[reg].byte_offset; +} + +static unsigned GetRegSize(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register number."); + return g_register_infos[reg].byte_size; +} + +RegisterContext_i386::RegisterContext_i386(Thread &thread, + uint32_t concrete_frame_idx) + : RegisterContextPOSIX(thread, concrete_frame_idx) +{ +} + +RegisterContext_i386::~RegisterContext_i386() +{ +} + +ProcessMonitor & +RegisterContext_i386::GetMonitor() +{ + ProcessPOSIX *process = static_cast<ProcessPOSIX*>(CalculateProcess()); + return process->GetMonitor(); +} + +void +RegisterContext_i386::Invalidate() +{ +} + +void +RegisterContext_i386::InvalidateAllRegisters() +{ +} + +size_t +RegisterContext_i386::GetRegisterCount() +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContext_i386::GetRegisterInfoAtIndex(uint32_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + else + return NULL; +} + +size_t +RegisterContext_i386::GetRegisterSetCount() +{ + return k_num_register_sets; +} + +const RegisterSet * +RegisterContext_i386::GetRegisterSet(uint32_t set) +{ + if (set < k_num_register_sets) + return &g_reg_sets[set]; + else + return NULL; +} + +unsigned +RegisterContext_i386::GetRegisterIndexFromOffset(unsigned offset) +{ + unsigned reg; + for (reg = 0; reg < k_num_registers; reg++) + { + if (g_register_infos[reg].byte_offset == offset) + break; + } + assert(reg < k_num_registers && "Invalid register offset."); + return reg; +} + +const char * +RegisterContext_i386::GetRegisterName(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register offset."); + return g_register_infos[reg].name; +} + +bool +RegisterContext_i386::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadRegisterValue(GetRegOffset(reg), GetRegSize(reg), value); +} + +bool +RegisterContext_i386::ReadAllRegisterValues(DataBufferSP &data_sp) +{ + return false; +} + +bool RegisterContext_i386::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteRegisterValue(GetRegOffset(reg), value); +} + +bool +RegisterContext_i386::WriteAllRegisterValues(const DataBufferSP &data) +{ + return false; +} + +bool +RegisterContext_i386::UpdateAfterBreakpoint() +{ + // PC points one byte past the int3 responsible for the breakpoint. + lldb::addr_t pc; + + if ((pc = GetPC()) == LLDB_INVALID_ADDRESS) + return false; + + SetPC(pc - 1); + return true; +} + +uint32_t +RegisterContext_i386::ConvertRegisterKindToRegisterNumber(uint32_t kind, + uint32_t num) +{ + if (kind == eRegisterKindGeneric) + { + switch (num) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_eip; + case LLDB_REGNUM_GENERIC_SP: return gpr_esp; + case LLDB_REGNUM_GENERIC_FP: return gpr_ebp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags; + case LLDB_REGNUM_GENERIC_RA: + default: + return LLDB_INVALID_REGNUM; + } + } + + if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (num) + { + case dwarf_eax: return gpr_eax; + case dwarf_edx: return gpr_edx; + case dwarf_ecx: return gpr_ecx; + case dwarf_ebx: return gpr_ebx; + case dwarf_esi: return gpr_esi; + case dwarf_edi: return gpr_edi; + case dwarf_ebp: return gpr_ebp; + case dwarf_esp: return gpr_esp; + case dwarf_eip: return gpr_eip; + case dwarf_xmm0: return fpu_xmm0; + case dwarf_xmm1: return fpu_xmm1; + case dwarf_xmm2: return fpu_xmm2; + case dwarf_xmm3: return fpu_xmm3; + case dwarf_xmm4: return fpu_xmm4; + case dwarf_xmm5: return fpu_xmm5; + case dwarf_xmm6: return fpu_xmm6; + case dwarf_xmm7: return fpu_xmm7; + case dwarf_stmm0: return fpu_stmm0; + case dwarf_stmm1: return fpu_stmm1; + case dwarf_stmm2: return fpu_stmm2; + case dwarf_stmm3: return fpu_stmm3; + case dwarf_stmm4: return fpu_stmm4; + case dwarf_stmm5: return fpu_stmm5; + case dwarf_stmm6: return fpu_stmm6; + case dwarf_stmm7: return fpu_stmm7; + default: + return LLDB_INVALID_REGNUM; + } + } + + if (kind == eRegisterKindGDB) + { + switch (num) + { + case gdb_eax : return gpr_eax; + case gdb_ebx : return gpr_ebx; + case gdb_ecx : return gpr_ecx; + case gdb_edx : return gpr_edx; + case gdb_esi : return gpr_esi; + case gdb_edi : return gpr_edi; + case gdb_ebp : return gpr_ebp; + case gdb_esp : return gpr_esp; + case gdb_eip : return gpr_eip; + case gdb_eflags : return gpr_eflags; + case gdb_cs : return gpr_cs; + case gdb_ss : return gpr_ss; + case gdb_ds : return gpr_ds; + case gdb_es : return gpr_es; + case gdb_fs : return gpr_fs; + case gdb_gs : return gpr_gs; + case gdb_stmm0 : return fpu_stmm0; + case gdb_stmm1 : return fpu_stmm1; + case gdb_stmm2 : return fpu_stmm2; + case gdb_stmm3 : return fpu_stmm3; + case gdb_stmm4 : return fpu_stmm4; + case gdb_stmm5 : return fpu_stmm5; + case gdb_stmm6 : return fpu_stmm6; + case gdb_stmm7 : return fpu_stmm7; + case gdb_fcw : return fpu_fcw; + case gdb_fsw : return fpu_fsw; + case gdb_ftw : return fpu_ftw; + case gdb_fpu_cs : return fpu_cs; + case gdb_ip : return fpu_ip; + case gdb_fpu_ds : return fpu_fos; + case gdb_dp : return fpu_foo; + case gdb_fop : return fpu_fop; + case gdb_xmm0 : return fpu_xmm0; + case gdb_xmm1 : return fpu_xmm1; + case gdb_xmm2 : return fpu_xmm2; + case gdb_xmm3 : return fpu_xmm3; + case gdb_xmm4 : return fpu_xmm4; + case gdb_xmm5 : return fpu_xmm5; + case gdb_xmm6 : return fpu_xmm6; + case gdb_xmm7 : return fpu_xmm7; + case gdb_mxcsr : return fpu_mxcsr; + default: + return LLDB_INVALID_REGNUM; + } + } + else if (kind == eRegisterKindLLDB) + { + return num; + } + + return LLDB_INVALID_REGNUM; +} + +bool +RegisterContext_i386::HardwareSingleStep(bool enable) +{ + enum { TRACE_BIT = 0x100 }; + uint64_t eflags; + + if ((eflags = ReadRegisterAsUnsigned(gpr_eflags, -1UL)) == -1UL) + return false; + + if (enable) + { + if (eflags & TRACE_BIT) + return true; + + eflags |= TRACE_BIT; + } + else + { + if (!(eflags & TRACE_BIT)) + return false; + + eflags &= ~TRACE_BIT; + } + + return WriteRegisterFromUnsigned(gpr_eflags, eflags); +} + +void +RegisterContext_i386::LogGPR(const char *title) +{ + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS)); + if (log) + { + if (title) + log->Printf ("%s", title); + for (uint32_t i=0; i<k_num_gpr_registers; i++) + { + uint32_t reg = gpr_eax + i; + log->Printf("%12s = 0x%8.8x", g_register_infos[reg].name, (&user.regs)[reg]); + } + } +} + +bool +RegisterContext_i386::ReadGPR() +{ + bool result; + + ProcessMonitor &monitor = GetMonitor(); + result = monitor.ReadGPR(&user.regs); + LogGPR("RegisterContext_i386::ReadGPR()"); + return result; +} + +bool +RegisterContext_i386::ReadFPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadFPR(&user.i387); +} diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContext_i386.h b/lldb/source/Plugins/Process/POSIX/RegisterContext_i386.h new file mode 100644 index 00000000000..1586501a8fb --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/RegisterContext_i386.h @@ -0,0 +1,169 @@ +//===-- RegisterContext_i386.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_RegisterContext_i386_h_ +#define liblldb_RegisterContext_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "RegisterContextPOSIX.h" + +class RegisterContext_i386 : public RegisterContextPOSIX +{ +public: + RegisterContext_i386(lldb_private::Thread &thread, + uint32_t concreate_frame_idx); + + ~RegisterContext_i386(); + + void + Invalidate(); + + void + InvalidateAllRegisters(); + + size_t + GetRegisterCount(); + + const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex(uint32_t reg); + + size_t + GetRegisterSetCount(); + + const lldb_private::RegisterSet * + GetRegisterSet(uint32_t set); + + static unsigned + GetRegisterIndexFromOffset(unsigned offset); + + static const char * + GetRegisterName(unsigned reg); + + bool + ReadRegisterValue(uint32_t reg, lldb_private::Scalar &value); + + bool + ReadRegisterBytes(uint32_t reg, lldb_private::DataExtractor &data); + + virtual bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + bool + WriteRegisterValue(uint32_t reg, const lldb_private::Scalar &value); + + bool + WriteRegisterBytes(uint32_t reg, lldb_private::DataExtractor &data, + uint32_t data_offset = 0); + + virtual bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + uint32_t + ConvertRegisterKindToRegisterNumber(uint32_t kind, uint32_t num); + + bool + HardwareSingleStep(bool enable); + + bool + UpdateAfterBreakpoint(); + + struct GPR + { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + uint32_t orig_ax; + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t esp; + uint32_t ss; + }; + + struct MMSReg + { + uint8_t bytes[8]; + }; + + struct XMMReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + uint16_t fcw; + uint16_t fsw; + uint16_t ftw; + uint16_t fop; + uint32_t ip; + uint32_t cs; + uint32_t foo; + uint32_t fos; + uint32_t mxcsr; + uint32_t reserved; + MMSReg stmm[8]; + XMMReg xmm[8]; + uint32_t pad[56]; + }; + + // A user area like this no longer exists on FreeBSD + // making this a Linux artifact. Nonetheless, it is safe + // leaving it here while the code is being cleaned up and generalized. + + struct UserArea + { + GPR regs; // General purpose registers. + int32_t fpvalid; // True if FPU is being used. + FPU i387; // FPU registers. + uint32_t tsize; // Text segment size. + uint32_t dsize; // Data segment size. + uint32_t ssize; // Stack segment size. + uint32_t start_code; // VM address of text. + uint32_t start_stack; // VM address of stack bottom (top in rsp). + int32_t signal; // Signal causing core dump. + int32_t reserved; // Unused. + uint32_t ar0; // Location of GPR's. + FPU* fpstate; // Location of FPR's. + uint32_t magic; // Identifier for core dumps. + char u_comm[32]; // Command causing core dump. + uint32_t u_debugreg[8]; // Debug registers (DR0 - DR7). + }; +private: + UserArea user; + + ProcessMonitor &GetMonitor(); + + void LogGPR(const char *title); + + bool ReadGPR(); + bool ReadFPR(); +}; + +#endif // #ifndef liblldb_RegisterContext_i386_h_ diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp new file mode 100644 index 00000000000..8ecb4d14cb8 --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp @@ -0,0 +1,757 @@ +//===-- RegisterContext_x86_64.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <cstring> +#include <errno.h> +#include <stdint.h> + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Target/Thread.h" +#include "lldb/Host/Endian.h" + +#include "ProcessPOSIX.h" +#include "ProcessMonitor.h" +#include "RegisterContext_x86_64.h" + +using namespace lldb_private; +using namespace lldb; + +// Internal codes for all x86_64 registers. +enum +{ + k_first_gpr, + gpr_rax = k_first_gpr, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs, + gpr_ss, + gpr_ds, + gpr_es, + k_last_gpr = gpr_es, + + k_first_fpr, + fpu_fcw = k_first_fpr, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15, + k_last_fpr = fpu_xmm15, + + k_num_registers, + k_num_gpr_registers = k_last_gpr - k_first_gpr + 1, + k_num_fpu_registers = k_last_fpr - k_first_fpr + 1 +}; + +// Number of register sets provided by this context. +enum +{ + k_num_register_sets = 2 +}; + +enum gcc_dwarf_regnums +{ + gcc_dwarf_gpr_rax = 0, + gcc_dwarf_gpr_rdx, + gcc_dwarf_gpr_rcx, + gcc_dwarf_gpr_rbx, + gcc_dwarf_gpr_rsi, + gcc_dwarf_gpr_rdi, + gcc_dwarf_gpr_rbp, + gcc_dwarf_gpr_rsp, + gcc_dwarf_gpr_r8, + gcc_dwarf_gpr_r9, + gcc_dwarf_gpr_r10, + gcc_dwarf_gpr_r11, + gcc_dwarf_gpr_r12, + gcc_dwarf_gpr_r13, + gcc_dwarf_gpr_r14, + gcc_dwarf_gpr_r15, + gcc_dwarf_gpr_rip, + gcc_dwarf_fpu_xmm0, + gcc_dwarf_fpu_xmm1, + gcc_dwarf_fpu_xmm2, + gcc_dwarf_fpu_xmm3, + gcc_dwarf_fpu_xmm4, + gcc_dwarf_fpu_xmm5, + gcc_dwarf_fpu_xmm6, + gcc_dwarf_fpu_xmm7, + gcc_dwarf_fpu_xmm8, + gcc_dwarf_fpu_xmm9, + gcc_dwarf_fpu_xmm10, + gcc_dwarf_fpu_xmm11, + gcc_dwarf_fpu_xmm12, + gcc_dwarf_fpu_xmm13, + gcc_dwarf_fpu_xmm14, + gcc_dwarf_fpu_xmm15, + gcc_dwarf_fpu_stmm0, + gcc_dwarf_fpu_stmm1, + gcc_dwarf_fpu_stmm2, + gcc_dwarf_fpu_stmm3, + gcc_dwarf_fpu_stmm4, + gcc_dwarf_fpu_stmm5, + gcc_dwarf_fpu_stmm6, + gcc_dwarf_fpu_stmm7 +}; + +enum gdb_regnums +{ + gdb_gpr_rax = 0, + gdb_gpr_rbx = 1, + gdb_gpr_rcx = 2, + gdb_gpr_rdx = 3, + gdb_gpr_rsi = 4, + gdb_gpr_rdi = 5, + gdb_gpr_rbp = 6, + gdb_gpr_rsp = 7, + gdb_gpr_r8 = 8, + gdb_gpr_r9 = 9, + gdb_gpr_r10 = 10, + gdb_gpr_r11 = 11, + gdb_gpr_r12 = 12, + gdb_gpr_r13 = 13, + gdb_gpr_r14 = 14, + gdb_gpr_r15 = 15, + gdb_gpr_rip = 16, + gdb_gpr_rflags = 17, + gdb_gpr_cs = 18, + gdb_gpr_ss = 19, + gdb_gpr_ds = 20, + gdb_gpr_es = 21, + gdb_gpr_fs = 22, + gdb_gpr_gs = 23, + gdb_fpu_stmm0 = 24, + gdb_fpu_stmm1 = 25, + gdb_fpu_stmm2 = 26, + gdb_fpu_stmm3 = 27, + gdb_fpu_stmm4 = 28, + gdb_fpu_stmm5 = 29, + gdb_fpu_stmm6 = 30, + gdb_fpu_stmm7 = 31, + gdb_fpu_fcw = 32, + gdb_fpu_fsw = 33, + gdb_fpu_ftw = 34, + gdb_fpu_cs = 35, + gdb_fpu_ip = 36, + gdb_fpu_ds = 37, + gdb_fpu_dp = 38, + gdb_fpu_fop = 39, + gdb_fpu_xmm0 = 40, + gdb_fpu_xmm1 = 41, + gdb_fpu_xmm2 = 42, + gdb_fpu_xmm3 = 43, + gdb_fpu_xmm4 = 44, + gdb_fpu_xmm5 = 45, + gdb_fpu_xmm6 = 46, + gdb_fpu_xmm7 = 47, + gdb_fpu_xmm8 = 48, + gdb_fpu_xmm9 = 49, + gdb_fpu_xmm10 = 50, + gdb_fpu_xmm11 = 51, + gdb_fpu_xmm12 = 52, + gdb_fpu_xmm13 = 53, + gdb_fpu_xmm14 = 54, + gdb_fpu_xmm15 = 55, + gdb_fpu_mxcsr = 56 +}; + +static const +uint32_t g_gpr_regnums[k_num_gpr_registers] = +{ + gpr_rax, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs, + gpr_ss, + gpr_ds, + gpr_es +}; + +static const uint32_t +g_fpu_regnums[k_num_fpu_registers] = +{ + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15 +}; + +static const RegisterSet +g_reg_sets[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums } +}; + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) \ + (offsetof(RegisterContext_x86_64::UserArea, regs) + \ + offsetof(GPR, regname)) + +// Computes the offset of the given FPR in the user data area. +#define FPR_OFFSET(regname) \ + (offsetof(RegisterContext_x86_64::UserArea, i387) + \ + offsetof(RegisterContext_x86_64::FPU, regname)) + +// Number of bytes needed to represent a GPR. +#define GPR_SIZE(reg) sizeof(((GPR*)NULL)->reg) + +// Number of bytes needed to represent a FPR. +#define FPR_SIZE(reg) sizeof(((RegisterContext_x86_64::FPU*)NULL)->reg) + +// Number of bytes needed to represent the i'th FP register. +#define FP_SIZE sizeof(((RegisterContext_x86_64::MMSReg*)NULL)->bytes) + +// Number of bytes needed to represent an XMM register. +#define XMM_SIZE sizeof(RegisterContext_x86_64::XMMReg) + +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg } } + +#define DEFINE_FPR(reg, kind1, kind2, kind3, kind4) \ + { #reg, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, fpu_##reg } } + +#define DEFINE_FP(reg, i) \ + { #reg#i, NULL, FP_SIZE, FPR_OFFSET(reg[i]), eEncodingVector, \ + eFormatVectorOfUInt8, \ + { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \ + LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i } } + +#define DEFINE_XMM(reg, i) \ + { #reg#i, NULL, XMM_SIZE, FPR_OFFSET(reg[i]), eEncodingVector, \ + eFormatVectorOfUInt8, \ + { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \ + LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i } } + +#define REG_CONTEXT_SIZE (sizeof(GPR) + sizeof(RegisterContext_x86_64::FPU)) + +static RegisterInfo +g_register_infos[k_num_registers] = +{ + // General purpose registers. + DEFINE_GPR(rax, NULL, gcc_dwarf_gpr_rax, gcc_dwarf_gpr_rax, LLDB_INVALID_REGNUM, gdb_gpr_rax), + DEFINE_GPR(rbx, NULL, gcc_dwarf_gpr_rbx, gcc_dwarf_gpr_rbx, LLDB_INVALID_REGNUM, gdb_gpr_rbx), + DEFINE_GPR(rcx, NULL, gcc_dwarf_gpr_rcx, gcc_dwarf_gpr_rcx, LLDB_INVALID_REGNUM, gdb_gpr_rcx), + DEFINE_GPR(rdx, NULL, gcc_dwarf_gpr_rdx, gcc_dwarf_gpr_rdx, LLDB_INVALID_REGNUM, gdb_gpr_rdx), + DEFINE_GPR(rdi, NULL, gcc_dwarf_gpr_rdi, gcc_dwarf_gpr_rdi, LLDB_INVALID_REGNUM, gdb_gpr_rdi), + DEFINE_GPR(rsi, NULL, gcc_dwarf_gpr_rsi, gcc_dwarf_gpr_rsi, LLDB_INVALID_REGNUM, gdb_gpr_rsi), + DEFINE_GPR(rbp, "fp", gcc_dwarf_gpr_rbp, gcc_dwarf_gpr_rbp, LLDB_REGNUM_GENERIC_FP, gdb_gpr_rbp), + DEFINE_GPR(rsp, "sp", gcc_dwarf_gpr_rsp, gcc_dwarf_gpr_rsp, LLDB_REGNUM_GENERIC_SP, gdb_gpr_rsp), + DEFINE_GPR(r8, NULL, gcc_dwarf_gpr_r8, gcc_dwarf_gpr_r8, LLDB_INVALID_REGNUM, gdb_gpr_r8), + DEFINE_GPR(r9, NULL, gcc_dwarf_gpr_r9, gcc_dwarf_gpr_r9, LLDB_INVALID_REGNUM, gdb_gpr_r9), + DEFINE_GPR(r10, NULL, gcc_dwarf_gpr_r10, gcc_dwarf_gpr_r10, LLDB_INVALID_REGNUM, gdb_gpr_r10), + DEFINE_GPR(r11, NULL, gcc_dwarf_gpr_r11, gcc_dwarf_gpr_r11, LLDB_INVALID_REGNUM, gdb_gpr_r11), + DEFINE_GPR(r12, NULL, gcc_dwarf_gpr_r12, gcc_dwarf_gpr_r12, LLDB_INVALID_REGNUM, gdb_gpr_r12), + DEFINE_GPR(r13, NULL, gcc_dwarf_gpr_r13, gcc_dwarf_gpr_r13, LLDB_INVALID_REGNUM, gdb_gpr_r13), + DEFINE_GPR(r14, NULL, gcc_dwarf_gpr_r14, gcc_dwarf_gpr_r14, LLDB_INVALID_REGNUM, gdb_gpr_r14), + DEFINE_GPR(r15, NULL, gcc_dwarf_gpr_r15, gcc_dwarf_gpr_r15, LLDB_INVALID_REGNUM, gdb_gpr_r15), + DEFINE_GPR(rip, "pc", gcc_dwarf_gpr_rip, gcc_dwarf_gpr_rip, LLDB_REGNUM_GENERIC_PC, gdb_gpr_rip), + DEFINE_GPR(rflags, "flags", LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, gdb_gpr_rflags), + DEFINE_GPR(cs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_cs), + DEFINE_GPR(fs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_fs), + DEFINE_GPR(gs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_gs), + DEFINE_GPR(ss, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_ss), + DEFINE_GPR(ds, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_ds), + DEFINE_GPR(es, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_es), + + // i387 Floating point registers. + DEFINE_FPR(fcw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fcw), + DEFINE_FPR(fsw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fsw), + DEFINE_FPR(ftw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ftw), + DEFINE_FPR(fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fop), + DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ip), + // FIXME: Extract segment from ip. + DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_cs), + DEFINE_FPR(dp, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_dp), + // FIXME: Extract segment from dp. + DEFINE_FPR(dp, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ds), + DEFINE_FPR(mxcsr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_mxcsr), + DEFINE_FPR(mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + // FP registers. + DEFINE_FP(stmm, 0), + DEFINE_FP(stmm, 1), + DEFINE_FP(stmm, 2), + DEFINE_FP(stmm, 3), + DEFINE_FP(stmm, 4), + DEFINE_FP(stmm, 5), + DEFINE_FP(stmm, 6), + DEFINE_FP(stmm, 7), + + // XMM registers + DEFINE_XMM(xmm, 0), + DEFINE_XMM(xmm, 1), + DEFINE_XMM(xmm, 2), + DEFINE_XMM(xmm, 3), + DEFINE_XMM(xmm, 4), + DEFINE_XMM(xmm, 5), + DEFINE_XMM(xmm, 6), + DEFINE_XMM(xmm, 7), + DEFINE_XMM(xmm, 8), + DEFINE_XMM(xmm, 9), + DEFINE_XMM(xmm, 10), + DEFINE_XMM(xmm, 11), + DEFINE_XMM(xmm, 12), + DEFINE_XMM(xmm, 13), + DEFINE_XMM(xmm, 14), + DEFINE_XMM(xmm, 15) +}; + +static unsigned GetRegOffset(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register number."); + return g_register_infos[reg].byte_offset; +} + +static unsigned GetRegSize(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register number."); + return g_register_infos[reg].byte_size; +} + +static bool IsGPR(unsigned reg) +{ + return reg <= k_last_gpr; // GPR's come first. +} + +static bool IsFPR(unsigned reg) +{ + return (k_first_fpr <= reg && reg <= k_last_fpr); +} + +RegisterContext_x86_64::RegisterContext_x86_64(Thread &thread, + uint32_t concrete_frame_idx) + : RegisterContextPOSIX(thread, concrete_frame_idx) +{ +} + +RegisterContext_x86_64::~RegisterContext_x86_64() +{ +} + +ProcessMonitor & +RegisterContext_x86_64::GetMonitor() +{ + ProcessPOSIX *process = static_cast<ProcessPOSIX*>(CalculateProcess()); + return process->GetMonitor(); +} + +void +RegisterContext_x86_64::Invalidate() +{ +} + +void +RegisterContext_x86_64::InvalidateAllRegisters() +{ +} + +size_t +RegisterContext_x86_64::GetRegisterCount() +{ + return k_num_registers; +} + +const RegisterInfo * +RegisterContext_x86_64::GetRegisterInfoAtIndex(uint32_t reg) +{ + if (reg < k_num_registers) + return &g_register_infos[reg]; + else + return NULL; +} + +size_t +RegisterContext_x86_64::GetRegisterSetCount() +{ + return k_num_register_sets; +} + +const RegisterSet * +RegisterContext_x86_64::GetRegisterSet(uint32_t set) +{ + if (set < k_num_register_sets) + return &g_reg_sets[set]; + else + return NULL; +} + +unsigned +RegisterContext_x86_64::GetRegisterIndexFromOffset(unsigned offset) +{ + unsigned reg; + for (reg = 0; reg < k_num_registers; reg++) + { + if (g_register_infos[reg].byte_offset == offset) + break; + } + assert(reg < k_num_registers && "Invalid register offset."); + return reg; +} + +const char * +RegisterContext_x86_64::GetRegisterName(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register offset."); + return g_register_infos[reg].name; +} + +bool +RegisterContext_x86_64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadRegisterValue(GetRegOffset(reg), GetRegSize(reg), value); +} + +bool +RegisterContext_x86_64::ReadAllRegisterValues(DataBufferSP &data_sp) +{ + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && ReadGPR () && ReadFPR ()) + { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy (dst, &user.regs, sizeof(user.regs)); + dst += sizeof(user.regs); + + ::memcpy (dst, &user.i387, sizeof(user.i387)); + return true; + } + return false; +} + +bool +RegisterContext_x86_64::WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteRegisterValue(GetRegOffset(reg), value); +} + +bool +RegisterContext_x86_64::WriteAllRegisterValues(const DataBufferSP &data_sp) +{ + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy (&user.regs, src, sizeof(user.regs)); + src += sizeof(user.regs); + + ::memcpy (&user.i387, src, sizeof(user.i387)); + return WriteGPR() & WriteFPR(); + } + return false; +} + +bool +RegisterContext_x86_64::UpdateAfterBreakpoint() +{ + // PC points one byte past the int3 responsible for the breakpoint. + lldb::addr_t pc; + + if ((pc = GetPC()) == LLDB_INVALID_ADDRESS) + return false; + + SetPC(pc - 1); + return true; +} + +uint32_t +RegisterContext_x86_64::ConvertRegisterKindToRegisterNumber(uint32_t kind, + uint32_t num) +{ + if (kind == eRegisterKindGeneric) + { + switch (num) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_rip; + case LLDB_REGNUM_GENERIC_SP: return gpr_rsp; + case LLDB_REGNUM_GENERIC_FP: return gpr_rbp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_rflags; + case LLDB_REGNUM_GENERIC_RA: + default: + return LLDB_INVALID_REGNUM; + } + } + + if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (num) + { + case gcc_dwarf_gpr_rax: return gpr_rax; + case gcc_dwarf_gpr_rdx: return gpr_rdx; + case gcc_dwarf_gpr_rcx: return gpr_rcx; + case gcc_dwarf_gpr_rbx: return gpr_rbx; + case gcc_dwarf_gpr_rsi: return gpr_rsi; + case gcc_dwarf_gpr_rdi: return gpr_rdi; + case gcc_dwarf_gpr_rbp: return gpr_rbp; + case gcc_dwarf_gpr_rsp: return gpr_rsp; + case gcc_dwarf_gpr_r8: return gpr_r8; + case gcc_dwarf_gpr_r9: return gpr_r9; + case gcc_dwarf_gpr_r10: return gpr_r10; + case gcc_dwarf_gpr_r11: return gpr_r11; + case gcc_dwarf_gpr_r12: return gpr_r12; + case gcc_dwarf_gpr_r13: return gpr_r13; + case gcc_dwarf_gpr_r14: return gpr_r14; + case gcc_dwarf_gpr_r15: return gpr_r15; + case gcc_dwarf_gpr_rip: return gpr_rip; + case gcc_dwarf_fpu_xmm0: return fpu_xmm0; + case gcc_dwarf_fpu_xmm1: return fpu_xmm1; + case gcc_dwarf_fpu_xmm2: return fpu_xmm2; + case gcc_dwarf_fpu_xmm3: return fpu_xmm3; + case gcc_dwarf_fpu_xmm4: return fpu_xmm4; + case gcc_dwarf_fpu_xmm5: return fpu_xmm5; + case gcc_dwarf_fpu_xmm6: return fpu_xmm6; + case gcc_dwarf_fpu_xmm7: return fpu_xmm7; + case gcc_dwarf_fpu_xmm8: return fpu_xmm8; + case gcc_dwarf_fpu_xmm9: return fpu_xmm9; + case gcc_dwarf_fpu_xmm10: return fpu_xmm10; + case gcc_dwarf_fpu_xmm11: return fpu_xmm11; + case gcc_dwarf_fpu_xmm12: return fpu_xmm12; + case gcc_dwarf_fpu_xmm13: return fpu_xmm13; + case gcc_dwarf_fpu_xmm14: return fpu_xmm14; + case gcc_dwarf_fpu_xmm15: return fpu_xmm15; + case gcc_dwarf_fpu_stmm0: return fpu_stmm0; + case gcc_dwarf_fpu_stmm1: return fpu_stmm1; + case gcc_dwarf_fpu_stmm2: return fpu_stmm2; + case gcc_dwarf_fpu_stmm3: return fpu_stmm3; + case gcc_dwarf_fpu_stmm4: return fpu_stmm4; + case gcc_dwarf_fpu_stmm5: return fpu_stmm5; + case gcc_dwarf_fpu_stmm6: return fpu_stmm6; + case gcc_dwarf_fpu_stmm7: return fpu_stmm7; + default: + return LLDB_INVALID_REGNUM; + } + } + + if (kind == eRegisterKindGDB) + { + switch (num) + { + case gdb_gpr_rax : return gpr_rax; + case gdb_gpr_rbx : return gpr_rbx; + case gdb_gpr_rcx : return gpr_rcx; + case gdb_gpr_rdx : return gpr_rdx; + case gdb_gpr_rsi : return gpr_rsi; + case gdb_gpr_rdi : return gpr_rdi; + case gdb_gpr_rbp : return gpr_rbp; + case gdb_gpr_rsp : return gpr_rsp; + case gdb_gpr_r8 : return gpr_r8; + case gdb_gpr_r9 : return gpr_r9; + case gdb_gpr_r10 : return gpr_r10; + case gdb_gpr_r11 : return gpr_r11; + case gdb_gpr_r12 : return gpr_r12; + case gdb_gpr_r13 : return gpr_r13; + case gdb_gpr_r14 : return gpr_r14; + case gdb_gpr_r15 : return gpr_r15; + case gdb_gpr_rip : return gpr_rip; + case gdb_gpr_rflags : return gpr_rflags; + case gdb_gpr_cs : return gpr_cs; + case gdb_gpr_ss : return gpr_ss; + case gdb_gpr_ds : return gpr_ds; + case gdb_gpr_es : return gpr_es; + case gdb_gpr_fs : return gpr_fs; + case gdb_gpr_gs : return gpr_gs; + case gdb_fpu_stmm0 : return fpu_stmm0; + case gdb_fpu_stmm1 : return fpu_stmm1; + case gdb_fpu_stmm2 : return fpu_stmm2; + case gdb_fpu_stmm3 : return fpu_stmm3; + case gdb_fpu_stmm4 : return fpu_stmm4; + case gdb_fpu_stmm5 : return fpu_stmm5; + case gdb_fpu_stmm6 : return fpu_stmm6; + case gdb_fpu_stmm7 : return fpu_stmm7; + case gdb_fpu_fcw : return fpu_fcw; + case gdb_fpu_fsw : return fpu_fsw; + case gdb_fpu_ftw : return fpu_ftw; + case gdb_fpu_cs : return fpu_cs; + case gdb_fpu_ip : return fpu_ip; + case gdb_fpu_ds : return fpu_ds; + case gdb_fpu_dp : return fpu_dp; + case gdb_fpu_fop : return fpu_fop; + case gdb_fpu_xmm0 : return fpu_xmm0; + case gdb_fpu_xmm1 : return fpu_xmm1; + case gdb_fpu_xmm2 : return fpu_xmm2; + case gdb_fpu_xmm3 : return fpu_xmm3; + case gdb_fpu_xmm4 : return fpu_xmm4; + case gdb_fpu_xmm5 : return fpu_xmm5; + case gdb_fpu_xmm6 : return fpu_xmm6; + case gdb_fpu_xmm7 : return fpu_xmm7; + case gdb_fpu_xmm8 : return fpu_xmm8; + case gdb_fpu_xmm9 : return fpu_xmm9; + case gdb_fpu_xmm10 : return fpu_xmm10; + case gdb_fpu_xmm11 : return fpu_xmm11; + case gdb_fpu_xmm12 : return fpu_xmm12; + case gdb_fpu_xmm13 : return fpu_xmm13; + case gdb_fpu_xmm14 : return fpu_xmm14; + case gdb_fpu_xmm15 : return fpu_xmm15; + case gdb_fpu_mxcsr : return fpu_mxcsr; + default: + return LLDB_INVALID_REGNUM; + } + } + else if (kind == eRegisterKindLLDB) + { + return num; + } + + return LLDB_INVALID_REGNUM; +} + +bool +RegisterContext_x86_64::HardwareSingleStep(bool enable) +{ + enum { TRACE_BIT = 0x100 }; + uint64_t rflags; + + if ((rflags = ReadRegisterAsUnsigned(gpr_rflags, -1UL)) == -1UL) + return false; + + if (enable) + { + if (rflags & TRACE_BIT) + return true; + + rflags |= TRACE_BIT; + } + else + { + if (!(rflags & TRACE_BIT)) + return false; + + rflags &= ~TRACE_BIT; + } + + return WriteRegisterFromUnsigned(gpr_rflags, rflags); +} + +bool +RegisterContext_x86_64::ReadGPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadGPR(&user.regs); +} + +bool +RegisterContext_x86_64::ReadFPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadFPR(&user.i387); +} + +bool +RegisterContext_x86_64::WriteGPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteGPR(&user.regs); +} + +bool +RegisterContext_x86_64::WriteFPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteFPR(&user.i387); +} diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h new file mode 100644 index 00000000000..56a1843f062 --- /dev/null +++ b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h @@ -0,0 +1,143 @@ +//===-- RegisterContext_x86_64.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_RegisterContext_x86_64_H_ +#define liblldb_RegisterContext_x86_64_H_ + +#include "lldb/Core/Log.h" +#include "RegisterContextPOSIX.h" + +#ifdef __FreeBSD__ +#include "RegisterContextFreeBSD_x86_64.h" +#endif + +#ifdef __linux__ +#include "RegisterContextLinux_x86_64.h" +#endif + +class ProcessMonitor; + +class RegisterContext_x86_64 + : public RegisterContextPOSIX +{ +public: + RegisterContext_x86_64 (lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + ~RegisterContext_x86_64(); + + void + Invalidate(); + + void + InvalidateAllRegisters(); + + size_t + GetRegisterCount(); + + const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex(uint32_t reg); + + size_t + GetRegisterSetCount(); + + const lldb_private::RegisterSet * + GetRegisterSet(uint32_t set); + + static unsigned + GetRegisterIndexFromOffset(unsigned offset); + + static const char * + GetRegisterName(unsigned reg); + + virtual bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + uint32_t + ConvertRegisterKindToRegisterNumber(uint32_t kind, uint32_t num); + + bool + HardwareSingleStep(bool enable); + + bool + UpdateAfterBreakpoint(); + + struct MMSReg + { + uint8_t bytes[10]; + uint8_t pad[6]; + }; + + struct XMMReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + uint16_t fcw; + uint16_t fsw; + uint16_t ftw; + uint16_t fop; + uint64_t ip; + uint64_t dp; + uint32_t mxcsr; + uint32_t mxcsrmask; + MMSReg stmm[8]; + XMMReg xmm[16]; + uint32_t padding[24]; + }; + + struct UserArea + { + GPR regs; // General purpose registers. + int32_t fpvalid; // True if FPU is being used. + int32_t pad0; + FPU i387; // FPU registers. + uint64_t tsize; // Text segment size. + uint64_t dsize; // Data segment size. + uint64_t ssize; // Stack segment size. + uint64_t start_code; // VM address of text. + uint64_t start_stack; // VM address of stack bottom (top in rsp). + int64_t signal; // Signal causing core dump. + int32_t reserved; // Unused. + int32_t pad1; + uint64_t ar0; // Location of GPR's. + FPU* fpstate; // Location of FPR's. + uint64_t magic; // Identifier for core dumps. + char u_comm[32]; // Command causing core dump. + uint64_t u_debugreg[8]; // Debug registers (DR0 - DR7). + uint64_t error_code; // CPU error code. + uint64_t fault_address; // Control register CR3. + }; + +private: + UserArea user; + + ProcessMonitor &GetMonitor(); + + bool ReadGPR(); + bool ReadFPR(); + + bool WriteGPR(); + bool WriteFPR(); +}; + +#endif // #ifndef liblldb_RegisterContext_x86_64_H_ |