fxos/shell/s.cpp

293 lines
8.2 KiB
C++

#include "shell.h"
#include "parser.h"
#include "commands.h"
#include "errors.h"
#include "theme.h"
#include <fxos/symbols.h>
#include <fmt/core.h>
#include <endian.h>
//---
// sh
//---
static bool matches(
char const *data, char const *reference, char const *pattern, size_t size)
{
for(size_t i = 0; i < size; i++) {
if(pattern[i] && data[i] != reference[i])
return false;
}
return true;
}
static int hexa(int c)
{
if(c >= '0' && c <= '9')
return c - '0';
return (c | 0x20) - 'a' + 10;
}
struct _sh_args
{
/* String to find */
std::string reference;
/* Bytes to match within reference (not all are required) */
std::string pattern;
/* Size of search */
size_t size;
/* Regions to search in, may be empty */
std::vector<MemoryRegion> regions;
/* Distance to show hexump around a match */
int distance;
/* Required alignment for matches to be used */
int align;
};
static _sh_args parse_sh(Session &session, Parser &parser)
{
_sh_args args;
args.distance = -1;
args.align = -1;
parser.option("align", [&args](std::string const &value) {
args.align = atoi(value.c_str());
});
parser.option("distance", [&args](std::string const &value) {
args.distance = atoi(value.c_str());
});
parser.accept_options();
std::string needle = parser.str();
/* Check the structure of the needle */
if(needle.size() == 0 || needle.size() % 2 != 0)
throw CommandError(
"search pattern '{}' should be of even non-zero "
"size",
needle);
size_t bad_index = needle.find_first_not_of("0123456789abcdefABCDEF.");
if(bad_index != std::string::npos)
throw CommandError(
"invalid character '{}' in seach pattern", needle[bad_index]);
for(size_t i = 0; i < needle.size(); i += 2) {
char c1 = needle[i], c2 = needle[i + 1];
if((c1 == '.') != (c2 == '.'))
throw CommandError(
"invalid search byte '{}{}', should be either "
"'..' or fully specified",
c1, c2);
}
/* Convert into a reference/pattern form */
args.size = needle.size() / 2;
args.reference.reserve(args.size);
args.pattern.reserve(args.size);
for(size_t i = 0; i < args.size; i++) {
char c1 = needle[2 * i], c2 = needle[2 * i + 1];
if(c1 == '.') {
args.reference[i] = 0;
args.pattern[i] = 0;
}
else {
args.reference[i] = (hexa(c1) << 4) | hexa(c2);
args.pattern[i] = 1;
}
}
while(!parser.at_end()) {
parser.accept_options();
args.regions.push_back(parser.region(session.current_space));
}
parser.accept_options();
parser.end();
return args;
}
void _sh(Session &session, char const *reference, char const *pattern,
size_t size, int align, int distance, std::vector<MemoryRegion> &regions)
{
if(!session.current_space)
return;
/* Default values */
if(distance < 0)
distance = 32;
if(align <= 0)
align = 1;
VirtualSpace &v = *session.current_space;
/* If no region is specified, explore the regions for all bindings */
if(regions.size() == 0) {
for(auto &b: v.bindings)
regions.push_back(b.region);
}
int match_count = 0;
bool output_started = false;
/* Matches are not shown right away because if they are close enough a
single local hexdump will show several of them */
std::vector<std::pair<uint32_t, int>> pending;
for(auto const &r: regions) {
char const *data = v.translate(r.start, r.size());
if(!data)
throw CommandError(
"region 0x{:08x} .. 0x{:08x} is not "
"fully bound",
r.start, r.end);
/* Reach the required alignemnt */
int i = 0;
while((r.start + i) % align != 0)
i++;
/* Search patterns for (size) bytes inside (data) */
for(; i <= (int)r.size() - (int)size; i += align) {
if(!matches(data + i, reference, pattern, size))
continue;
uint32_t start = r.start + i;
/* Flush pending matches if this new match is far away */
if(pending.size() > 0) {
auto const &p = pending[pending.size() - 1];
if(p.first + p.second + distance < start) {
Range r;
r.start = pending[0].first - distance;
r.end = p.first + p.second + distance - 1;
if(output_started)
fmt::print("...\n");
_h_hexdump(session, r, pending);
output_started = true;
pending.clear();
}
}
pending.emplace_back(start, size);
match_count++;
start += size;
if(match_count >= 128)
break;
}
if(match_count >= 128)
break;
/* Print the last pending elements */
if(pending.size()) {
auto const &p = pending[pending.size() - 1];
Range r
= {pending[0].first - distance, p.first + p.second + distance};
if(output_started)
fmt::print("...\n");
_h_hexdump(session, r, pending);
}
pending.clear();
}
if(match_count == 0)
fmt::print("No occurrence found.\n");
else if(match_count < 128)
fmt::print("{} occurences found.\n", match_count);
else
fmt::print("Stopped after 128 occurrences.\n");
}
//---
// s4
//---
struct _s4_args
{
uint32_t value;
std::vector<MemoryRegion> regions;
};
static _s4_args parse_s4(Session &session, Parser &parser)
{
_s4_args args;
args.value = parser.expr(session.current_space);
while(!parser.at_end()) {
args.regions.push_back(parser.region(session.current_space));
}
parser.end();
return args;
}
void _s4(Session &session, uint32_t value, std::vector<MemoryRegion> &regions)
{
uint32_t value_big_endian = htobe32(value);
char pattern[4] = {1, 1, 1, 1};
_sh(session, (char *)&value_big_endian, pattern, 4, 4, -1, regions);
}
//---
// Command registration
//---
static ShellCommand _s4_cmd(
"s4",
[](Session &s, Parser &p) {
auto args = parse_s4(s, p);
_s4(s, args.value, args.regions);
},
[](Session &s, Parser &p) { parse_s4(s, p); }, "Search 4-aligned value", R"(
s4 <value> [<regions>...]
Searches mapped memory for a 4-aligned 32-bit value. If regions are specified
(by name or the <start>:<size> syntax), searches these regions. Otherwise,
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.
)");
static ShellCommand _sh_cmd(
"sh",
[](Session &s, Parser &p) {
auto args = parse_sh(s, p);
_sh(s, args.reference.c_str(), args.pattern.c_str(), args.size,
args.align, args.distance, args.regions);
},
[](Session &s, Parser &p) { parse_sh(s, p); }, "Search Hexadecimal pattern",
R"(
sh [align=<value>] "<pattern>" [<regions>...]
Searches mapped memory for a hexadecimal pattern with hole bytes. The pattern
should be a hexadecimal string like "0123..6789..cd", consisting either of
explicit hexadecimal values or hole bytes marked by "..". The holes must start
and end at byte boundaries.
An occurrence of the pattern is any sequence of bytes that matches the explicit
bytes exactly, with no constraint on the hole bytes (ie. any value matches).
If regions are specified, searches these regions. Otherwise, searches every
binding in the current virtual space. If align=<align> is specified, only
considers addresses that are multiples of the specified alignment value.
sh "434153494f......00" ROM
Searches strings ending with "CASIO" followed by up to 3 characters in ROM.
sh align=4 "b4..0000"
Searches 4-aligned values close to the display interface 0xb4000000 within
all currently bound regions.
)");