Serenity Operating System
at master 173 lines 5.5 kB view raw
1/* 2 * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Assertions.h> 8#include <AK/HashMap.h> 9#include <AK/NonnullOwnPtr.h> 10#include <AK/StringBuilder.h> 11#include <Kernel/API/SyscallString.h> 12#include <LibCore/ArgsParser.h> 13#include <LibCore/System.h> 14#include <LibDebug/DebugSession.h> 15#include <LibELF/Image.h> 16#include <LibMain/Main.h> 17#include <LibX86/Disassembler.h> 18#include <LibX86/Instruction.h> 19#include <signal.h> 20#include <stdlib.h> 21#include <string.h> 22#include <sys/arch/regs.h> 23#include <syscall.h> 24#include <unistd.h> 25 26static OwnPtr<Debug::DebugSession> g_debug_session; 27static bool g_should_output_color = false; 28 29static void handle_sigint(int) 30{ 31 outln("Debugger: SIGINT"); 32 33 // The destructor of DebugSession takes care of detaching 34 g_debug_session = nullptr; 35} 36 37static void print_function_call(DeprecatedString function_name, size_t depth) 38{ 39 for (size_t i = 0; i < depth; ++i) { 40 out(" "); 41 } 42 outln("=> {}", function_name); 43} 44 45static void print_syscall(PtraceRegisters& regs, size_t depth) 46{ 47 for (size_t i = 0; i < depth; ++i) { 48 out(" "); 49 } 50 StringView begin_color = g_should_output_color ? "\033[34;1m"sv : ""sv; 51 StringView end_color = g_should_output_color ? "\033[0m"sv : ""sv; 52#if ARCH(X86_64) 53 outln("=> {}SC_{}({:#x}, {:#x}, {:#x}){}", 54 begin_color, 55 Syscall::to_string((Syscall::Function)regs.rax), 56 regs.rdx, 57 regs.rcx, 58 regs.rbx, 59 end_color); 60#elif ARCH(AARCH64) 61 (void)regs; 62 (void)begin_color; 63 (void)end_color; 64 TODO_AARCH64(); 65#else 66# error Unknown architecture 67#endif 68} 69 70static NonnullOwnPtr<HashMap<FlatPtr, X86::Instruction>> instrument_code() 71{ 72 auto instrumented = make<HashMap<FlatPtr, X86::Instruction>>(); 73 g_debug_session->for_each_loaded_library([&](Debug::LoadedLibrary const& lib) { 74 lib.debug_info->elf().for_each_section_of_type(SHT_PROGBITS, [&](const ELF::Image::Section& section) { 75 if (section.name() != ".text") 76 return IterationDecision::Continue; 77 78 X86::SimpleInstructionStream stream((const u8*)((uintptr_t)lib.file->data() + section.offset()), section.size()); 79 X86::Disassembler disassembler(stream); 80 for (;;) { 81 auto offset = stream.offset(); 82 auto instruction_address = section.address() + offset + lib.base_address; 83 auto insn = disassembler.next(); 84 if (!insn.has_value()) 85 break; 86 if (insn.value().mnemonic() == "RET" || insn.value().mnemonic() == "CALL") { 87 g_debug_session->insert_breakpoint(instruction_address); 88 instrumented->set(instruction_address, insn.value()); 89 } 90 } 91 return IterationDecision::Continue; 92 }); 93 return IterationDecision::Continue; 94 }); 95 return instrumented; 96} 97 98ErrorOr<int> serenity_main(Main::Arguments arguments) 99{ 100 TRY(Core::System::pledge("stdio proc exec rpath sigaction ptrace")); 101 102 if (isatty(STDOUT_FILENO)) 103 g_should_output_color = true; 104 105 StringView command; 106 Core::ArgsParser args_parser; 107 args_parser.add_positional_argument(command, 108 "The program to be traced, along with its arguments", 109 "program", Core::ArgsParser::Required::Yes); 110 args_parser.parse(arguments); 111 112 auto result = Debug::DebugSession::exec_and_attach(command); 113 if (!result) { 114 warnln("Failed to start debugging session for: \"{}\"", command); 115 exit(1); 116 } 117 g_debug_session = result.release_nonnull(); 118 119 auto instrumented = instrument_code(); 120 121 struct sigaction sa; 122 memset(&sa, 0, sizeof(struct sigaction)); 123 sa.sa_handler = handle_sigint; 124 TRY(Core::System::sigaction(SIGINT, &sa, nullptr)); 125 126 size_t depth = 0; 127 bool new_function = true; 128 129 g_debug_session->run(Debug::DebugSession::DesiredInitialDebugeeState::Running, [&](Debug::DebugSession::DebugBreakReason reason, Optional<PtraceRegisters> regs) { 130 if (reason == Debug::DebugSession::DebugBreakReason::Exited) { 131 outln("Program exited."); 132 return Debug::DebugSession::DebugDecision::Detach; 133 } 134 135 if (reason == Debug::DebugSession::DebugBreakReason::Syscall) { 136 print_syscall(regs.value(), depth + 1); 137 return Debug::DebugSession::DebugDecision::ContinueBreakAtSyscall; 138 } 139 140#if ARCH(X86_64) 141 const FlatPtr ip = regs.value().rip; 142#elif ARCH(AARCH64) 143 const FlatPtr ip = 0; // FIXME 144 TODO_AARCH64(); 145#else 146# error Unknown architecture 147#endif 148 149 if (new_function) { 150 auto function_name = g_debug_session->symbolicate(ip); 151 print_function_call(function_name.value().symbol, depth); 152 new_function = false; 153 return Debug::DebugSession::ContinueBreakAtSyscall; 154 } 155 auto instruction = instrumented->get(ip).value(); 156 157 if (instruction.mnemonic() == "RET") { 158 if (depth != 0) 159 --depth; 160 return Debug::DebugSession::ContinueBreakAtSyscall; 161 } 162 163 // FIXME: we could miss some leaf functions that were called with a jump 164 VERIFY(instruction.mnemonic() == "CALL"); 165 166 ++depth; 167 new_function = true; 168 169 return Debug::DebugSession::DebugDecision::SingleStep; 170 }); 171 172 return 0; 173}