add ss commmand to search for a string
This commit is contained in:
parent
0373ae50fe
commit
6b45ad0661
|
@ -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 &);
|
||||
|
|
|
@ -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)
|
||||
|
|
292
shell/s.cpp
292
shell/s.cpp
|
@ -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> ®ions)
|
|||
_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.
|
||||
)");
|
||||
|
|
Loading…
Reference in New Issue