Created
April 9, 2017 19:59
-
-
Save agustingianni/1b59ecb621ac9e269f7a5d5020c333d4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef DEBUGGER_H_ | |
| #define DEBUGGER_H_ | |
| #include <map> | |
| #include <string> | |
| #include <vector> | |
| #include <Conditionals.h> | |
| #include <lldb/API/LLDB.h> | |
| #include <optional.h> | |
| #if RETOOLS_TARGET_CPU_X86 | |
| static constexpr auto ARCH_GENERIC_STACK_POINTER = "esp"; | |
| static constexpr auto ARCH_GENERIC_FRAME_POINTER = "ebp"; | |
| #elif RETOOLS_TARGET_CPU_AMD64 | |
| static constexpr auto ARCH_GENERIC_STACK_POINTER = "rsp"; | |
| static constexpr auto ARCH_GENERIC_FRAME_POINTER = "rbp"; | |
| #elif RETOOLS_TARGET_CPU_ARM | |
| static constexpr auto ARCH_GENERIC_STACK_POINTER = "sp"; | |
| static constexpr auto ARCH_GENERIC_FRAME_POINTER = "bp"; | |
| #elif RETOOLS_TARGET_CPU_ARM64 | |
| static constexpr auto ARCH_GENERIC_STACK_POINTER = "sp"; | |
| static constexpr auto ARCH_GENERIC_FRAME_POINTER = "bp"; | |
| #else | |
| #error Invalid architecture | |
| #endif | |
| class Debugger { | |
| private: | |
| lldb::SBDebugger m_debugger; | |
| lldb::SBTarget m_target; | |
| lldb::SBProcess m_process; | |
| lldb::SBThread m_thread; | |
| lldb::SBListener m_listener; | |
| // Loaded binary file data. | |
| lldb::SBModule m_module; | |
| int m_launch_flags; | |
| std::map<std::string, uint32_t> m_libmap; | |
| public: | |
| Debugger(); | |
| ~Debugger(); | |
| // Delete copy and other stuff. | |
| Debugger(const Debugger& other) = delete; | |
| Debugger(Debugger&& other) = delete; | |
| Debugger& operator=(const Debugger&) = delete; | |
| Debugger& operator=(Debugger&&) = delete; | |
| lldb::SBDebugger& get_debugger() | |
| { | |
| return m_debugger; | |
| } | |
| lldb::SBTarget& get_target() | |
| { | |
| return m_target; | |
| } | |
| lldb::SBProcess& get_process() | |
| { | |
| return m_process; | |
| } | |
| lldb::SBThread& get_thread() | |
| { | |
| return m_thread; | |
| } | |
| lldb::SBModule& get_module() | |
| { | |
| return m_module; | |
| } | |
| void disable_aslr() | |
| { | |
| m_launch_flags |= lldb::eLaunchFlagDisableASLR; | |
| } | |
| void stop_at_entrypoint() | |
| { | |
| m_launch_flags |= lldb::eLaunchFlagStopAtEntry; | |
| } | |
| void handle_process_stopped_state(lldb::SBEvent process_event); | |
| void handle_process_event(lldb::SBEvent process_event); | |
| void enable_debug_log(const char* channel, std::vector<const char*> categories); | |
| void main_loop(); | |
| // Options passed to 'evaluate_expression'. | |
| struct ExpressionOptions { | |
| ExpressionOptions() | |
| : m_ignore_breakpoints{ false } | |
| , m_stop_other_threads{ true } | |
| , m_persist_expression{ false } | |
| , m_unwind_on_error{ true } | |
| , m_timeout{ 1000000 } | |
| { | |
| } | |
| bool m_ignore_breakpoints; | |
| bool m_stop_other_threads; | |
| bool m_persist_expression; | |
| bool m_unwind_on_error; | |
| uint32_t m_timeout; | |
| }; | |
| // Expression evaluation. | |
| template <typename T> | |
| std::optional<T> evaluate_expression(std::string expression, ExpressionOptions options = {}); | |
| // Utilities to alter the behavior of the remote process. | |
| bool library_load(std::string filename); | |
| bool library_unload(std::string filename); | |
| // Process execution. | |
| bool process_execute(std::string filename); | |
| bool process_execute(std::string filename, std::vector<std::string> args); | |
| bool process_execute(std::string filename, std::vector<std::string> args, std::vector<std::string> env); | |
| bool process_execute(std::string filename, std::vector<std::string> args, std::vector<std::string> env, std::string cwd); | |
| // Attach and detach routines. | |
| bool process_attach(std::string process_name, bool wait = false); | |
| bool process_attach(lldb::pid_t pid); | |
| bool process_detach(); | |
| // Main process related utilities. | |
| bool process_continue(); | |
| bool process_kill(); | |
| bool process_stop(); | |
| // Breakpoint handling. | |
| std::optional<lldb::SBBreakpoint> breakpoint_add(uintptr_t address); | |
| std::optional<lldb::SBBreakpoint> breakpoint_add(std::string function_name, std::string module_name = {}); | |
| bool breakpoint_del(lldb::SBBreakpoint& breakpoint); | |
| // Perform actions on all breakpoints. | |
| bool breakpoint_delete_all(); | |
| bool breakpoint_enable_all(); | |
| bool breakpoint_disable_all(); | |
| // Breakpoint saving / loading. | |
| bool breakpoint_save(std::string filename); | |
| bool breakpoint_load(std::string filename); | |
| // Data breakpoints access breakpoints. | |
| std::optional<lldb::SBWatchpoint> watchpoint_add(uintptr_t address, size_t size, bool read = true, bool write = true); | |
| bool watchpoint_del(lldb::SBWatchpoint& breakpoint); | |
| // Perform actions on all data breakpoints. | |
| bool watchpoint_delete_all(); | |
| bool watchpoint_enable_all(); | |
| bool watchpoint_disable_all(); | |
| // Typed memory operations. | |
| template <typename T> | |
| bool memory_read(uintptr_t address, T* value); | |
| template <typename T> | |
| bool memory_write(uintptr_t address, T value); | |
| template <typename T> | |
| bool memory_write(uintptr_t address, T value, size_t count); | |
| // Raw memory operations. | |
| bool memory_read(uintptr_t address, void* buffer, size_t size); | |
| bool memory_write(uintptr_t address, const void* buffer, size_t size); | |
| // Memory querying operations. | |
| std::optional<lldb::SBMemoryRegionInfoList> memory_regions(); | |
| std::optional<lldb::SBMemoryRegionInfo> memory_region(uintptr_t address); | |
| // Stepping functions for the current thread. | |
| bool step_out(); | |
| bool step_instruction(bool step_over = false); | |
| bool step_to(uintptr_t address); | |
| // Stepping functions for 'thread'. | |
| bool step_out(lldb::SBThread& thread); | |
| bool step_instruction(lldb::SBThread& thread, bool step_over = false); | |
| bool step_to(lldb::SBThread& thread, uintptr_t address); | |
| // Multiple register access routines. | |
| std::optional<lldb::SBValueList> registers_get(); | |
| std::optional<lldb::SBValueList> registers_get(lldb::SBThread& thread); | |
| bool registers_set(std::map<std::string, std::string> values); | |
| bool registers_set(lldb::SBThread& thread, std::map<std::string, std::string> values); | |
| // Single register access routines. | |
| std::optional<lldb::SBValue> register_get(std::string register_name); | |
| std::optional<lldb::SBValue> register_get(lldb::SBThread& thread, std::string register_name); | |
| bool register_set(std::string register_name, std::string register_value); | |
| bool register_set(lldb::SBThread& thread, std::string register_name, std::string register_value); | |
| // Getters for special purpose registers. Returns the value or empty if failed. | |
| std::optional<uintptr_t> get_fp(); | |
| std::optional<uintptr_t> get_fp(lldb::SBThread& thread); | |
| std::optional<uintptr_t> get_pc(); | |
| std::optional<uintptr_t> get_pc(lldb::SBThread& thread); | |
| std::optional<uintptr_t> get_sp(); | |
| std::optional<uintptr_t> get_sp(lldb::SBThread& thread); | |
| // Setters for special purpose registers. Returns the old value or empty if failed. | |
| std::optional<uintptr_t> set_fp(uintptr_t address); | |
| std::optional<uintptr_t> set_fp(lldb::SBThread& thread, uintptr_t address); | |
| std::optional<uintptr_t> set_pc(uintptr_t address); | |
| std::optional<uintptr_t> set_pc(lldb::SBThread& thread, uintptr_t address); | |
| std::optional<uintptr_t> set_sp(uintptr_t address); | |
| std::optional<uintptr_t> set_sp(lldb::SBThread& thread, uintptr_t address); | |
| // Methods to work with the stack. | |
| template <typename T> | |
| bool stack_push(T value); | |
| template <typename T> | |
| bool stack_push(lldb::SBThread& thread, T value); | |
| template <typename T> | |
| bool stack_pop(T* value); | |
| template <typename T> | |
| bool stack_pop(lldb::SBThread& thread, T* value); | |
| }; | |
| // Evaluate an expression in the context of the current process. | |
| template <typename T> | |
| std::optional<T> Debugger::evaluate_expression(std::string expression, ExpressionOptions options) | |
| { | |
| lldb::SBExpressionOptions expression_options; | |
| expression_options.SetIgnoreBreakpoints(options.m_ignore_breakpoints); | |
| expression_options.SetStopOthers(options.m_stop_other_threads); | |
| expression_options.SetSuppressPersistentResult(!options.m_persist_expression); | |
| expression_options.SetUnwindOnError(options.m_unwind_on_error); | |
| lldb::SBValue ret = m_target.EvaluateExpression(expression.c_str(), expression_options); | |
| if (!ret.IsValid()) { | |
| printf("Error while evaluating expression: %s\n", ret.GetError().GetCString()); | |
| return {}; | |
| } | |
| lldb::SBData data = ret.GetData(); | |
| if (!data.IsValid()) { | |
| printf("Error while getting expression data.\n"); | |
| return {}; | |
| } | |
| T value; | |
| lldb::SBError error; | |
| data.ReadRawData(error, 0, &value, sizeof(T)); | |
| if (!error.IsValid()) { | |
| printf("Error converting value: %s\n", error.GetCString()); | |
| return {}; | |
| } | |
| return value; | |
| } | |
| #endif /* DEBUGGER_H_ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment