add ss commmand to search for a string

This commit is contained in:
Dr-Carlos 2022-12-15 10:48:32 +10:30
parent 0373ae50fe
commit 6b45ad0661
3 changed files with 335 additions and 17 deletions

View File

@ -50,6 +50,10 @@ public:
PrintPass(Disassembly &disasm);
bool analyzeInstruction(uint32_t pc, Instruction &inst) override;
/* Allows the user to specify more options */
bool analyzeInstructionFull(uint32_t pc, Instruction &inst,
std::optional<std::string *> output, bool print_syscall_names);
//---
// Print pass parameters
//---
@ -98,7 +102,7 @@ private:
/** Internal promotion tree printers **/
void queue(std::string, bool = false);
void queue_flush();
std::string queue_flush();
std::vector<std::string> m_messages;
void pcjumploc(Argument const &);

View File

@ -9,6 +9,8 @@
#include <fxos/vspace.h>
#include <fxos/util/format.h>
#include <fmt/core.h>
#include <fmt/printf.h>
#include <cstdarg>
#include <cstring>
@ -26,29 +28,35 @@ PrintPass::PrintPass(Disassembly &disasm):
m_symtables.push_back(disasm.vspace.symbols);
}
bool PrintPass::analyzeInstruction(uint32_t pc, Instruction &i)
bool PrintPass::analyzeInstructionFull(uint32_t pc, Instruction &i,
std::optional<std::string *> output, bool print_syscall_names)
{
std::string 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 +68,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 +77,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 +86,26 @@ 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;
if(output)
(*output)->append(out);
else
std::cout << out;
return true;
}
bool PrintPass::analyzeInstruction(uint32_t pc, Instruction &i)
{
return analyzeInstructionFull(pc, i, {}, true);
}
std::optional<std::string> PrintPass::symquery(
Symbol::Type type, uint32_t value)
{
@ -108,15 +128,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)

View File

@ -4,9 +4,15 @@
#include "errors.h"
#include "theme.h"
#include <regex>
#include <fxos/symbols.h>
#include <fmt/core.h>
#include <endian.h>
#include <fmt/printf.h>
#include <fxos/util/log.h>
#include <fxos/passes/print.h>
#include <fxos/passes/syscall.h>
#include <fxos/passes/pcrel.h>
//---
// sh
@ -235,6 +241,265 @@ void _s4(Session &session, uint32_t value, std::vector<MemoryRegion> &regions)
_sh(session, (char *)&value_big_endian, pattern, 4, 4, -1, regions);
}
//---
// ss
//---
struct _ss_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 _ss_args parse_ss(Session &session, Parser &parser)
{
_ss_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 _ss(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;
/* Stores disassembly output */
std::string disasm_str;
for(auto &pair: disasm.instructions)
ok &= p.analyzeInstructionFull(
pair.first, pair.second, &disasm_str, false);
if(!ok) {
FxOS_log(ERR, "ss print pass failed");
return;
}
if(disasm_str.empty()) {
FxOS_log(ERR, "no disassembly found in virtual space");
return;
}
/* Used for std::getline */
std::istringstream iss(disasm_str);
/* 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::tuple<FxOS::Symbol::Type, uint32_t>, std::string> matches;
/* Unknown matches to print */
std::string unknown_matches;
/* Do a regex search on each line */
for(std::string line; std::getline(iss, 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<std::string> 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<std::string> 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 +520,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 +554,29 @@ sh align=4 "b4..0000"
Searches 4-aligned values close to the display interface 0xb4000000 within
all currently bound regions.
)");
static ShellCommand _ss_cmd(
"ss",
[](Session &s, Parser &p) {
auto args = parse_ss(s, p);
_ss(s, args.pattern.c_str(), args.identify_claims, args.vspace_name);
},
[](Session &s, Parser &p) { parse_ss(s, p); }, "Search string",
R"(
ss [claims=true] [vspace=<virtual_space>] "<pattern>"
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.
ss "%12a8"
Searches for all usages of syscall 0x12a8 and print which syscalls they are
within.
ss claims=false "%12a8"
Searches for all usages of syscall 0x12a8.
ss claims=false vspace=cg_2.00 "%12a8"
Searches for all usages of syscall 0x12a8 within the 'cg_2.00' virtual space.
)");