diff options
Diffstat (limited to 'source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp')
-rw-r--r-- | source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp | 266 |
1 files changed, 235 insertions, 31 deletions
diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp index 82e45a5d5fc1..93d294fd040a 100644 --- a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp +++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp @@ -10,12 +10,21 @@ // C Includes #include <errno.h> +#include <pthread.h> +#include <pthread_np.h> +#include <stdlib.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <sys/user.h> +#include <machine/elf.h> // C++ Includes #include <mutex> +#include <unordered_map> // Other libraries and framework includes #include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" #include "lldb/Core/State.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/ObjectFile.h" @@ -23,11 +32,11 @@ #include "lldb/Target/Target.h" #include "FreeBSDThread.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "Plugins/Process/Utility/FreeBSDSignals.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "ProcessFreeBSD.h" #include "ProcessMonitor.h" -#include "ProcessPOSIXLog.h" // Other libraries and framework includes #include "lldb/Breakpoint/BreakpointLocation.h" @@ -36,15 +45,19 @@ #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" -#include "lldb/Host/FileSpec.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/FileSpec.h" #include "lldb/Host/posix/Fcntl.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Threading.h" + using namespace lldb; using namespace lldb_private; @@ -70,12 +83,11 @@ ProcessFreeBSD::CreateInstance(lldb::TargetSP target_sp, } void ProcessFreeBSD::Initialize() { - static std::once_flag g_once_flag; + static llvm::once_flag g_once_flag; - std::call_once(g_once_flag, []() { + llvm::call_once(g_once_flag, []() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); - ProcessPOSIXLog::Initialize(GetPluginNameStatic()); }); } @@ -122,6 +134,7 @@ Error ProcessFreeBSD::DoResume() { std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex()); bool do_step = false; + bool software_single_step = !SupportHardwareSingleStepping(); for (tid_collection::const_iterator t_pos = m_run_tids.begin(), t_end = m_run_tids.end(); @@ -133,6 +146,11 @@ Error ProcessFreeBSD::DoResume() { t_pos != t_end; ++t_pos) { m_monitor->ThreadSuspend(*t_pos, false); do_step = true; + if (software_single_step) { + Error error = SetupSoftwareSingleStepping(*t_pos); + if (error.Fail()) + return error; + } } for (tid_collection::const_iterator t_pos = m_suspend_tids.begin(), t_end = m_suspend_tids.end(); @@ -145,7 +163,7 @@ Error ProcessFreeBSD::DoResume() { if (log) log->Printf("process %" PRIu64 " resuming (%s)", GetID(), do_step ? "step" : "continue"); - if (do_step) + if (do_step && !software_single_step) m_monitor->SingleStep(GetID(), m_resume_signo); else m_monitor->Resume(GetID(), m_resume_signo); @@ -281,8 +299,7 @@ Error ProcessFreeBSD::DoAttachToProcessWithID( assert(m_monitor == NULL); Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) - log->Printf("ProcessFreeBSD::%s(pid = %" PRIi64 ")", __FUNCTION__, GetID()); + LLDB_LOGV(log, "pid = {0}", GetID()); m_monitor = new ProcessMonitor(this, pid, error); @@ -354,9 +371,9 @@ Error ProcessFreeBSD::DoLaunch(Module *module, ProcessLaunchInfo &launch_info) { assert(m_monitor == NULL); FileSpec working_dir = launch_info.GetWorkingDirectory(); - if (working_dir && - (!working_dir.ResolvePath() || - working_dir.GetFileType() != FileSpec::eFileTypeDirectory)) { + namespace fs = llvm::sys::fs; + if (working_dir && (!working_dir.ResolvePath() || + !fs::is_directory(working_dir.GetPath()))) { error.SetErrorStringWithFormat("No such file or directory: %s", working_dir.GetCString()); return error; @@ -528,9 +545,7 @@ ProcessFreeBSD::CreateNewFreeBSDThread(lldb_private::Process &process, void ProcessFreeBSD::RefreshStateAfterStop() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) - log->Printf("ProcessFreeBSD::%s(), message_queue size = %d", __FUNCTION__, - (int)m_message_queue.size()); + LLDB_LOGV(log, "message_queue size = {0}", m_message_queue.size()); std::lock_guard<std::recursive_mutex> guard(m_message_mutex); @@ -542,10 +557,8 @@ void ProcessFreeBSD::RefreshStateAfterStop() { // Resolve the thread this message corresponds to and pass it along. lldb::tid_t tid = message.GetTID(); - if (log) - log->Printf( - "ProcessFreeBSD::%s(), message_queue size = %d, pid = %" PRIi64, - __FUNCTION__, (int)m_message_queue.size(), tid); + LLDB_LOGV(log, " message_queue size = {0}, pid = {1}", + m_message_queue.size(), tid); m_thread_list.RefreshStateAfterStop(); @@ -557,10 +570,7 @@ void ProcessFreeBSD::RefreshStateAfterStop() { if (message.GetKind() == ProcessMessage::eExitMessage) { // FIXME: We should tell the user about this, but the limbo message is // probably better for that. - if (log) - log->Printf("ProcessFreeBSD::%s() removing thread, tid = %" PRIi64, - __FUNCTION__, tid); - + LLDB_LOG(log, "removing thread, tid = {0}", tid); std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex()); ThreadSP thread_sp = m_thread_list.RemoveThreadByID(tid, false); @@ -903,13 +913,207 @@ bool ProcessFreeBSD::IsAThreadRunning() { const DataBufferSP ProcessFreeBSD::GetAuxvData() { // If we're the local platform, we can ask the host for auxv data. PlatformSP platform_sp = GetTarget().GetPlatform(); - if (platform_sp && platform_sp->IsHost()) - return lldb_private::Host::GetAuxvData(this); - - // Somewhat unexpected - the process is not running locally or we don't have a - // platform. - assert( - false && - "no platform or not the host - how did we get here with ProcessFreeBSD?"); - return DataBufferSP(); + assert(platform_sp && platform_sp->IsHost()); + + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_AUXV, (int)m_process->GetID()}; + size_t auxv_size = AT_COUNT * sizeof(Elf_Auxinfo); + DataBufferSP buf_sp(new DataBufferHeap(auxv_size, 0)); + + if (::sysctl(mib, 4, buf_sp->GetBytes(), &auxv_size, NULL, 0) != 0) { + perror("sysctl failed on auxv"); + buf_sp.reset(); + } + + return buf_sp; +} + +struct EmulatorBaton { + ProcessFreeBSD *m_process; + RegisterContext *m_reg_context; + + // eRegisterKindDWARF -> RegisterValue + std::unordered_map<uint32_t, RegisterValue> m_register_values; + + EmulatorBaton(ProcessFreeBSD *process, RegisterContext *reg_context) + : m_process(process), m_reg_context(reg_context) {} +}; + +static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, void *dst, size_t length) { + EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton); + + Error error; + size_t bytes_read = + emulator_baton->m_process->DoReadMemory(addr, dst, length, error); + if (!error.Success()) + bytes_read = 0; + return bytes_read; +} + +static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value) { + EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton); + + auto it = emulator_baton->m_register_values.find( + reg_info->kinds[eRegisterKindDWARF]); + if (it != emulator_baton->m_register_values.end()) { + reg_value = it->second; + return true; + } + + // The emulator only fills in the dwarf register numbers (and in some cases + // the generic register numbers). Get the full register info from the + // register context based on the dwarf register numbers. + const RegisterInfo *full_reg_info = + emulator_baton->m_reg_context->GetRegisterInfo( + eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]); + + bool error = + emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); + return error; +} + +static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value) { + EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton); + emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = + reg_value; + return true; +} + +static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, const void *dst, + size_t length) { + return length; +} + +bool ProcessFreeBSD::SingleStepBreakpointHit( + void *baton, lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { + return false; +} + +Error ProcessFreeBSD::SetSoftwareSingleStepBreakpoint(lldb::tid_t tid, + lldb::addr_t addr) { + Error error; + + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + if (log) { + log->Printf("ProcessFreeBSD::%s addr = 0x%" PRIx64, __FUNCTION__, addr); + log->Printf("SoftwareBreakpoint::%s addr = 0x%" PRIx64, __FUNCTION__, addr); + } + + // Validate the address. + if (addr == LLDB_INVALID_ADDRESS) + return Error("ProcessFreeBSD::%s invalid load address specified.", + __FUNCTION__); + + Breakpoint *const sw_step_break = + m_process->GetTarget().CreateBreakpoint(addr, true, false).get(); + sw_step_break->SetCallback(SingleStepBreakpointHit, this, true); + sw_step_break->SetBreakpointKind("software-signle-step"); + + if (log) + log->Printf("ProcessFreeBSD::%s addr = 0x%" PRIx64 " -- SUCCESS", + __FUNCTION__, addr); + + m_threads_stepping_with_breakpoint.insert({tid, sw_step_break->GetID()}); + return Error(); +} + +bool ProcessFreeBSD::IsSoftwareStepBreakpoint(lldb::tid_t tid) { + ThreadSP thread = GetThreadList().FindThreadByID(tid); + if (!thread) + return false; + + assert(thread->GetRegisterContext()); + lldb::addr_t stop_pc = thread->GetRegisterContext()->GetPC(); + + const auto &iter = m_threads_stepping_with_breakpoint.find(tid); + if (iter == m_threads_stepping_with_breakpoint.end()) + return false; + + lldb::break_id_t bp_id = iter->second; + BreakpointSP bp = GetTarget().GetBreakpointByID(bp_id); + if (!bp) + return false; + + BreakpointLocationSP bp_loc = bp->FindLocationByAddress(stop_pc); + if (!bp_loc) + return false; + + GetTarget().RemoveBreakpointByID(bp_id); + m_threads_stepping_with_breakpoint.erase(tid); + return true; +} + +bool ProcessFreeBSD::SupportHardwareSingleStepping() const { + lldb_private::ArchSpec arch = GetTarget().GetArchitecture(); + if (arch.GetMachine() == llvm::Triple::arm || + arch.GetMachine() == llvm::Triple::mips64 || + arch.GetMachine() == llvm::Triple::mips64el || + arch.GetMachine() == llvm::Triple::mips || + arch.GetMachine() == llvm::Triple::mipsel) + return false; + return true; +} + +Error ProcessFreeBSD::SetupSoftwareSingleStepping(lldb::tid_t tid) { + std::unique_ptr<EmulateInstruction> emulator_ap( + EmulateInstruction::FindPlugin(GetTarget().GetArchitecture(), + eInstructionTypePCModifying, nullptr)); + + if (emulator_ap == nullptr) + return Error("Instruction emulator not found!"); + + FreeBSDThread *thread = static_cast<FreeBSDThread *>( + m_thread_list.FindThreadByID(tid, false).get()); + if (thread == NULL) + return Error("Thread not found not found!"); + + lldb::RegisterContextSP register_context_sp = thread->GetRegisterContext(); + + EmulatorBaton baton(this, register_context_sp.get()); + emulator_ap->SetBaton(&baton); + emulator_ap->SetReadMemCallback(&ReadMemoryCallback); + emulator_ap->SetReadRegCallback(&ReadRegisterCallback); + emulator_ap->SetWriteMemCallback(&WriteMemoryCallback); + emulator_ap->SetWriteRegCallback(&WriteRegisterCallback); + + if (!emulator_ap->ReadInstruction()) + return Error("Read instruction failed!"); + + bool emulation_result = + emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC); + const RegisterInfo *reg_info_pc = register_context_sp->GetRegisterInfo( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + auto pc_it = + baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]); + + lldb::addr_t next_pc; + if (emulation_result) { + assert(pc_it != baton.m_register_values.end() && + "Emulation was successful but PC wasn't updated"); + next_pc = pc_it->second.GetAsUInt64(); + } else if (pc_it == baton.m_register_values.end()) { + // Emulate instruction failed and it haven't changed PC. Advance PC + // with the size of the current opcode because the emulation of all + // PC modifying instruction should be successful. The failure most + // likely caused by a not supported instruction which don't modify PC. + next_pc = + register_context_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize(); + } else { + // The instruction emulation failed after it modified the PC. It is an + // unknown error where we can't continue because the next instruction is + // modifying the PC but we don't know how. + return Error("Instruction emulation failed unexpectedly"); + } + + SetSoftwareSingleStepBreakpoint(tid, next_pc); + return Error(); } |