DebugSession.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*
  2. * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #pragma once
  27. #include <AK/Demangle.h>
  28. #include <AK/HashMap.h>
  29. #include <AK/MappedFile.h>
  30. #include <AK/NonnullRefPtr.h>
  31. #include <AK/Optional.h>
  32. #include <AK/OwnPtr.h>
  33. #include <AK/String.h>
  34. #include <LibC/sys/arch/i386/regs.h>
  35. #include <LibDebug/DebugInfo.h>
  36. #include <signal.h>
  37. #include <stdio.h>
  38. #include <sys/ptrace.h>
  39. #include <sys/wait.h>
  40. #include <unistd.h>
  41. namespace Debug {
  42. class DebugSession {
  43. public:
  44. static OwnPtr<DebugSession> exec_and_attach(const String& command, String source_root = {});
  45. ~DebugSession();
  46. int pid() const { return m_debuggee_pid; }
  47. bool poke(u32* address, u32 data);
  48. Optional<u32> peek(u32* address) const;
  49. enum class BreakPointState {
  50. Enabled,
  51. Disabled,
  52. };
  53. struct BreakPoint {
  54. void* address { nullptr };
  55. u32 original_first_word { 0 };
  56. BreakPointState state { BreakPointState::Disabled };
  57. };
  58. struct InsertBreakpointAtSymbolResult {
  59. String library_name;
  60. FlatPtr address { 0 };
  61. };
  62. Optional<InsertBreakpointAtSymbolResult> insert_breakpoint(const String& symbol_name);
  63. struct InsertBreakpointAtSourcePositionResult {
  64. String library_name;
  65. String file_name;
  66. size_t line_number { 0 };
  67. FlatPtr address { 0 };
  68. };
  69. Optional<InsertBreakpointAtSourcePositionResult> insert_breakpoint(const String& file_name, size_t line_number);
  70. bool insert_breakpoint(void* address);
  71. bool disable_breakpoint(void* address);
  72. bool enable_breakpoint(void* address);
  73. bool remove_breakpoint(void* address);
  74. bool breakpoint_exists(void* address) const;
  75. void dump_breakpoints()
  76. {
  77. for (auto addr : m_breakpoints.keys()) {
  78. dbg() << addr;
  79. }
  80. }
  81. PtraceRegisters get_registers() const;
  82. void set_registers(const PtraceRegisters&);
  83. enum class ContinueType {
  84. FreeRun,
  85. Syscall,
  86. };
  87. void continue_debuggee(ContinueType type = ContinueType::FreeRun);
  88. // Returns the wstatus result of waitpid()
  89. int continue_debuggee_and_wait(ContinueType type = ContinueType::FreeRun);
  90. // Returns the new eip
  91. void* single_step();
  92. void detach();
  93. enum DesiredInitialDebugeeState {
  94. Running,
  95. Stopped
  96. };
  97. template<typename Callback>
  98. void run(DesiredInitialDebugeeState, Callback);
  99. enum DebugDecision {
  100. Continue,
  101. SingleStep,
  102. ContinueBreakAtSyscall,
  103. Detach,
  104. Kill,
  105. };
  106. enum DebugBreakReason {
  107. Breakpoint,
  108. Syscall,
  109. Exited,
  110. };
  111. struct LoadedLibrary {
  112. String name;
  113. MappedFile file;
  114. NonnullOwnPtr<DebugInfo> debug_info;
  115. FlatPtr base_address;
  116. LoadedLibrary(const String& name, MappedFile&& file, NonnullOwnPtr<DebugInfo>&& debug_info, FlatPtr base_address)
  117. : name(name)
  118. , file(move(file))
  119. , debug_info(move(debug_info))
  120. , base_address(base_address)
  121. {
  122. }
  123. };
  124. template<typename Func>
  125. void for_each_loaded_library(Func f) const
  126. {
  127. for (const auto& lib_name : m_loaded_libraries.keys()) {
  128. const auto& lib = *m_loaded_libraries.get(lib_name).value();
  129. if (f(lib) == IterationDecision::Break)
  130. break;
  131. }
  132. }
  133. const LoadedLibrary* library_at(FlatPtr address) const;
  134. struct SymbolicationResult {
  135. String library_name;
  136. String symbol;
  137. };
  138. Optional<SymbolicationResult> symbolicate(FlatPtr address) const;
  139. Optional<DebugInfo::SourcePositionAndAddress> get_address_from_source_position(const String& file, size_t line) const;
  140. Optional<DebugInfo::SourcePosition> get_source_position(FlatPtr address) const;
  141. private:
  142. explicit DebugSession(pid_t, String source_root);
  143. // x86 breakpoint instruction "int3"
  144. static constexpr u8 BREAKPOINT_INSTRUCTION = 0xcc;
  145. static MappedFile map_executable_for_process(pid_t);
  146. void update_loaded_libs();
  147. int m_debuggee_pid { -1 };
  148. String m_source_root;
  149. bool m_is_debuggee_dead { false };
  150. HashMap<void*, BreakPoint> m_breakpoints;
  151. // Maps from base address to loaded library
  152. HashMap<String, NonnullOwnPtr<LoadedLibrary>> m_loaded_libraries;
  153. };
  154. template<typename Callback>
  155. void DebugSession::run(DesiredInitialDebugeeState initial_debugee_state, Callback callback)
  156. {
  157. enum class State {
  158. FirstIteration,
  159. FreeRun,
  160. Syscall,
  161. ConsecutiveBreakpoint,
  162. SingleStep,
  163. };
  164. State state { State::FirstIteration };
  165. auto do_continue_and_wait = [&]() {
  166. int wstatus = continue_debuggee_and_wait((state == State::Syscall) ? ContinueType::Syscall : ContinueType::FreeRun);
  167. // FIXME: This check actually only checks whether the debuggee
  168. // stopped because it hit a breakpoint/syscall/is in single stepping mode or not
  169. if (WSTOPSIG(wstatus) != SIGTRAP) {
  170. callback(DebugBreakReason::Exited, Optional<PtraceRegisters>());
  171. m_is_debuggee_dead = true;
  172. return true;
  173. }
  174. return false;
  175. };
  176. for (;;) {
  177. if ((state == State::FirstIteration && initial_debugee_state == DesiredInitialDebugeeState::Running) || state == State::FreeRun || state == State::Syscall) {
  178. if (do_continue_and_wait())
  179. break;
  180. }
  181. if (state == State::FirstIteration)
  182. state = State::FreeRun;
  183. auto regs = get_registers();
  184. Optional<BreakPoint> current_breakpoint;
  185. if (state == State::FreeRun || state == State::Syscall) {
  186. current_breakpoint = m_breakpoints.get((void*)((u32)regs.eip - 1));
  187. if (current_breakpoint.has_value())
  188. state = State::FreeRun;
  189. } else {
  190. current_breakpoint = m_breakpoints.get((void*)regs.eip);
  191. }
  192. if (current_breakpoint.has_value()) {
  193. // We want to make the breakpoint transparent to the user of the debugger.
  194. // To achieive this, we perform two rollbacks:
  195. // 1. Set regs.eip to point at the actual address of the instruction we breaked on.
  196. // regs.eip currently points to one byte after the address of the original instruction,
  197. // because the cpu has just executed the INT3 we patched into the instruction.
  198. // 2. We restore the original first byte of the instruction,
  199. // because it was patched with INT3.
  200. regs.eip = reinterpret_cast<u32>(current_breakpoint.value().address);
  201. set_registers(regs);
  202. disable_breakpoint(current_breakpoint.value().address);
  203. }
  204. DebugBreakReason reason = (state == State::Syscall && !current_breakpoint.has_value()) ? DebugBreakReason::Syscall : DebugBreakReason::Breakpoint;
  205. DebugDecision decision = callback(reason, regs);
  206. if (reason == DebugBreakReason::Syscall) {
  207. // skip the exit from the syscall
  208. if (do_continue_and_wait())
  209. break;
  210. }
  211. if (decision == DebugDecision::Continue) {
  212. state = State::FreeRun;
  213. } else if (decision == DebugDecision::ContinueBreakAtSyscall) {
  214. state = State::Syscall;
  215. }
  216. bool did_single_step = false;
  217. // Re-enable the breakpoint if it wasn't removed by the user
  218. if (current_breakpoint.has_value() && m_breakpoints.contains(current_breakpoint.value().address)) {
  219. // The current breakpoint was removed to make it transparent to the user.
  220. // We now want to re-enable it - the code execution flow could hit it again.
  221. // To re-enable the breakpoint, we first perform a single step and execute the
  222. // instruction of the breakpoint, and then redo the INT3 patch in its first byte.
  223. // If the user manually inserted a breakpoint at were we breaked at originally,
  224. // we need to disable that breakpoint because we want to singlestep over it to execute the
  225. // instruction we breaked on (we re-enable it again later anyways).
  226. if (m_breakpoints.contains(current_breakpoint.value().address) && m_breakpoints.get(current_breakpoint.value().address).value().state == BreakPointState::Enabled) {
  227. disable_breakpoint(current_breakpoint.value().address);
  228. }
  229. auto stopped_address = single_step();
  230. enable_breakpoint(current_breakpoint.value().address);
  231. did_single_step = true;
  232. // If there is another breakpoint after the current one,
  233. // Then we are already on it (because of single_step)
  234. auto breakpoint_at_next_instruction = m_breakpoints.get(stopped_address);
  235. if (breakpoint_at_next_instruction.has_value()
  236. && breakpoint_at_next_instruction.value().state == BreakPointState::Enabled) {
  237. state = State::ConsecutiveBreakpoint;
  238. }
  239. }
  240. if (decision == DebugDecision::SingleStep) {
  241. state = State::SingleStep;
  242. }
  243. if (decision == DebugDecision::Detach) {
  244. detach();
  245. break;
  246. }
  247. if (decision == DebugDecision::Kill) {
  248. ASSERT_NOT_REACHED(); // TODO: implement
  249. }
  250. if (state == State::SingleStep && !did_single_step) {
  251. single_step();
  252. }
  253. }
  254. }
  255. }