diff --git a/include/fxos/passes/print.h b/include/fxos/passes/print.h index ad54356..caa8896 100644 --- a/include/fxos/passes/print.h +++ b/include/fxos/passes/print.h @@ -40,6 +40,8 @@ #include #include +#include + namespace FxOS { class OS; @@ -50,6 +52,10 @@ public: PrintPass(Disassembly &disasm); bool analyzeInstruction(uint32_t pc, Instruction &inst) override; + /* Allows the user to specify more options */ + bool analyzeInstructionOutput( + uint32_t pc, Instruction &inst, std::ostream &out); + //--- // Print pass parameters //--- @@ -81,6 +87,9 @@ public: /* In a mova, promote "pc+" to the computed address */ int promote_pcaddr_loc; + /* Print syscall names when analyzing disassembly */ + bool print_syscall_names = true; + /* TODO: More print pass parameters */ private: @@ -98,7 +107,7 @@ private: /** Internal promotion tree printers **/ void queue(std::string, bool = false); - void queue_flush(); + std::string queue_flush(); std::vector m_messages; void pcjumploc(Argument const &); diff --git a/lib/passes/print.cpp b/lib/passes/print.cpp index c40a1e9..a393916 100644 --- a/lib/passes/print.cpp +++ b/lib/passes/print.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include @@ -26,29 +28,33 @@ PrintPass::PrintPass(Disassembly &disasm): m_symtables.push_back(disasm.vspace.symbols); } -bool PrintPass::analyzeInstruction(uint32_t pc, Instruction &i) +bool PrintPass::analyzeInstructionOutput( + uint32_t pc, Instruction &i, std::ostream &out) { /* Ellipsis if there is a gap since last instruction */ if(m_last_address + 1 != 0 && pc != m_last_address + 2) - printf(" ...\n"); + out << " ...\n"; /* Preliminary syscall number */ int syscall_id; - if(m_os && (syscall_id = m_os->find_syscall(pc)) >= 0) { - printf("\n<%%%04x", syscall_id); + if(print_syscall_names && m_os + && (syscall_id = m_os->find_syscall(pc)) >= 0) { + out << fmt::format("\n<%{:04x}", syscall_id); auto maybe_str = symquery(Symbol::Syscall, syscall_id); if(maybe_str) - printf(" %s", (*maybe_str).c_str()); - printf(">\n"); + out << " " + *maybe_str; + out << ">\n"; } /* Raw data if instruction cannot be decoded */ - printf(" %08x: %04x", pc, (i.inst ? i.inst->opcode : i.opcode)); + out << fmt::format( + " {:08x}: {:04x}", pc, (i.inst ? i.inst->opcode : i.opcode)); + if(!i.inst) { - printf("\n"); + out << "\n"; m_last_address = pc; return true; } @@ -60,7 +66,7 @@ bool PrintPass::analyzeInstruction(uint32_t pc, Instruction &i) int spacing = i.inst->arg_count ? 8 - strlen(i.inst->mnemonic) - strlen(suffix) : 0; - printf(" %s%s%*s", i.inst->mnemonic, suffix, spacing, ""); + out << fmt::sprintf(" %s%s%*s", i.inst->mnemonic, suffix, spacing, ""); /* Arguments */ @@ -69,7 +75,7 @@ bool PrintPass::analyzeInstruction(uint32_t pc, Instruction &i) Argument const &a = i.args[n]; if(n) - printf(", "); + out << ", "; queue(arg.str()); if(arg.kind == AsmArgument::PcJump) @@ -78,14 +84,21 @@ bool PrintPass::analyzeInstruction(uint32_t pc, Instruction &i) pcrelloc(a); else if(arg.kind == AsmArgument::PcAddr) pcaddrloc(a); - queue_flush(); + + out << queue_flush(); } - printf("\n"); + out << "\n"; m_last_address = pc; + return true; } +bool PrintPass::analyzeInstruction(uint32_t pc, Instruction &i) +{ + return analyzeInstructionOutput(pc, i, std::cout); +} + std::optional PrintPass::symquery( Symbol::Type type, uint32_t value) { @@ -108,15 +121,19 @@ void PrintPass::queue(std::string str, bool override) m_messages.push_back(str); } -void PrintPass::queue_flush() +std::string PrintPass::queue_flush() { + std::string out; + for(size_t i = 0; i < m_messages.size(); i++) { if(i != 0) - printf(" "); - printf("%s", m_messages[i].c_str()); + out += " "; + out += m_messages[i]; } m_messages.clear(); + + return out; } void PrintPass::pcjumploc(Argument const &a) diff --git a/shell/s.cpp b/shell/s.cpp index b9196c7..3be891a 100644 --- a/shell/s.cpp +++ b/shell/s.cpp @@ -4,9 +4,15 @@ #include "errors.h" #include "theme.h" +#include #include #include #include +#include +#include +#include +#include +#include //--- // sh @@ -235,6 +241,257 @@ void _s4(Session &session, uint32_t value, std::vector ®ions) _sh(session, (char *)&value_big_endian, pattern, 4, 4, -1, regions); } +//--- +// ssd +//--- + +struct _ssd_args +{ + /* String to find */ + std::string pattern; + /* Custom virtual space */ + std::string vspace_name; + /* Required alignment for matches to be used */ + bool identify_claims; +}; + +static _ssd_args parse_ssd(Session &session, Parser &parser) +{ + _ssd_args args; + args.identify_claims = true; + + parser.option("claims", [&args](std::string const &value) { + args.identify_claims = (value == "true"); + }); + + parser.option("vspace", + [&args](std::string const &value) { args.vspace_name = value; }); + + parser.accept_options(); + args.pattern = parser.str(); + + /* Check the structure of the needle */ + + if(args.pattern.size() == 0) + throw CommandError( + "search pattern '{}' should be non-zero " + "size", + args.pattern); + + /* Check virtual space name */ + + VirtualSpace *space = session.current_space; + if(!args.vspace_name.empty()) { + space = session.get_space(args.vspace_name); + if(!space) { + std::string msg + = format("virtual space '%s' does not exist", args.vspace_name); + if(parser.completing()) + throw Parser::CompletionRequest("_error", msg); + else + FxOS_log(ERR, "%s", msg); + } + } + + parser.accept_options(); + parser.end(); + + return args; +} + +void _ssd(Session &session, char const *pattern, bool identify_claims, + std::string vspace_name) +{ + VirtualSpace *space = session.current_space; + + if(!vspace_name.empty()) { + space = session.get_space(vspace_name); + + if(!space) { + FxOS_log(ERR, "no virtual space selected"); + return; + } + } + + if(!space) { + FxOS_log(ERR, "virtual space '%s' does not exist", vspace_name); + return; + } + + OS *os = space->os_analysis(); + if(!os) { + if(!vspace_name.empty()) + FxOS_log(ERR, "OS analysis on '%s' failed", vspace_name); + else + FxOS_log(ERR, "OS analysis failed"); + return; + } + + if(space->disasm.instructions.size() == 0) { + if(vspace_name.empty()) + FxOS_log(ERR, "current virtual space has no main disassembly"); + else + FxOS_log( + ERR, "virtual space '%s' has no main disassembly", vspace_name); + return; + } + + /* Address of the last disassembled instruction, i.e. where we should stop searching */ + uint32_t end_address = (++space->disasm.instructions.rend())->first; + + if(space->cursor >= end_address) + return; + + /* Create local disassembly information */ + FxOS::Disassembly disasm(*space); + + /* Load the block into memory */ + for(uint32_t pc = space->cursor; pc < end_address; pc += 2) + disasm.getInstructionAt(pc, true); + + /* To determine if passes fail */ + bool ok = true; + + /* Analyse pcrel */ + PcrelPass pcrel(disasm); + ok = pcrel.analyzeAllInstructions(); + + if(!ok) { + FxOS_log(ERR, "ss pcrel pass failed"); + return; + } + + /* Analyse syscalls */ + SyscallPass syscall(disasm, os); + ok = syscall.analyzeAllInstructions(); + + if(!ok) { + FxOS_log(ERR, "ss syscall pass failed"); + return; + } + + /* Create string output from disassembly */ + PrintPass p(disasm); + + p.promote_pcjump_loc = PrintPass::Promote; + p.promote_pcrel_loc = PrintPass::Promote; + p.promote_pcrel_value = PrintPass::Promote; + p.promote_syscall = PrintPass::Promote; + p.promote_syscallname = PrintPass::Append; + p.promote_symbol = PrintPass::Append; + p.promote_pcaddr_loc = PrintPass::Promote; + p.print_syscall_names = false; + + /* Stores disassembly output */ + std::stringstream disasm_str; + + for(auto &pair: disasm.instructions) + ok &= p.analyzeInstructionOutput(pair.first, pair.second, disasm_str); + + if(!ok) { + FxOS_log(ERR, "ss print pass failed"); + return; + } + + /* Store number of matches */ + int match_count = 0; + + /* Store regex results */ + std::smatch match; + + /* Store claim */ + Claim const *claim; + + /* Map of symbols -> matches */ + std::map, std::string> matches; + + /* Unknown matches to print */ + std::string unknown_matches; + + /* Do a regex search on each line */ + for(std::string line; std::getline(disasm_str, line);) + if(std::regex_search( + line, match, std::regex(pattern, std::regex::extended))) { + if(identify_claims) { + /* Search for the address, so that match.str(1) has the addr */ + std::regex_search(line, match, + std::regex("^ (.+): .+$", std::regex::extended)); + + std::istringstream address_str(match.str(1)); + uint32_t address; + address_str >> std::hex >> address; + + claim = space->disasm.getClaimAt(address); + if(claim) { + int syscall = os->find_syscall(claim->address); + + if(syscall == -1) + syscall = os->find_syscall(claim->owner); + + if(syscall == -1) + matches[{FxOS::Symbol::Address, claim->address}] + += line + "\n"; + else + matches[{FxOS::Symbol::Syscall, syscall}] + += line + "\n"; + } + else + unknown_matches += line + "\n"; + } + else + unknown_matches += line + "\n"; + + // fmt::print("{}\n", line); + + match_count++; + } + + if(identify_claims) { + for(auto [location, lines]: matches) { + auto [type, value] = location; + + if(type == FxOS::Symbol::Address) { + fmt::print(theme(10), "{:#08x} ", value); + + std::optional name + = space->symbols.query(FxOS::Symbol::Address, value); + + if(name) + fmt::print(theme(3), "{}", *name); + + fmt::print(":\n"); + } + else { + fmt::print("<"); + fmt::print(theme(10), "%{:04x}", value); + + std::optional name + = space->symbols.query(FxOS::Symbol::Syscall, value); + + if(name) + fmt::print(theme(3), " {}", *name); + + fmt::print(">\n"); + } + + fmt::print("{}\n", lines); + } + + if(unknown_matches.size() > 0) { + fmt::print(theme(10), "Unknown"); + fmt::print(":\n"); + } + } + + fmt::print("{}", unknown_matches); + + if(match_count == 0) + fmt::print("\nNo occurrence found.\n"); + else + fmt::print("\n{} occurences found.\n", match_count); +} + + //--- // Command registration //--- @@ -255,7 +512,6 @@ searches every binding in the current virtual space. s4 0xb4000000 ROM 0x88000000:512 Searches occurrences of 0xb4000000, 4-aligned, within mapped ROM and within the first 512 bytes of RAM. - s4 0x43415349 Seacrhes for the 4-aligned string "CASI" in all currently bound regions. )"); @@ -290,3 +546,30 @@ sh align=4 "b4..0000" Searches 4-aligned values close to the display interface 0xb4000000 within all currently bound regions. )"); + +static ShellCommand _ssd_cmd( + "ssd", + [](Session &s, Parser &p) { + auto args = parse_ssd(s, p); + _ssd(s, args.pattern.c_str(), args.identify_claims, args.vspace_name); + }, + [](Session &s, Parser &p) { parse_ssd(s, p); }, + "Search string in disassembly", + R"( +ssd [claims=true] [vspace=] "" + +Searches disassembly from $ to the last analysed instruction for a string +pattern. If claims=true, the resulting addresses are then checked for claims so +that the command can output whether it is within a syscall, and what the names +of those syscalls are. + +ssd "%12a8" + Searches for all usages of syscall 0x12a8 and print which syscalls they are + within. + +ssd claims=false "%12a8" + Searches for all usages of syscall 0x12a8. + +ssd claims=false vspace=cg_2.00 "%12a8" + Searches for all usages of syscall 0x12a8 within the 'cg_2.00' virtual space. +)");