fxos/shell/i.cpp

424 lines
12 KiB
C++

#include "shell.h"
#include "parser.h"
#include "commands.h"
#include "errors.h"
#include "theme.h"
#include <algorithm>
#include <fmt/core.h>
#include <fxos/util/log.h>
//---
// ic
//---
struct _ic_args
{
std::vector<uint32_t> addresses;
};
static struct _ic_args parse_ic(Session &session, Parser &parser)
{
_ic_args args;
while(!parser.at_end())
args.addresses.push_back(parser.expr(session.current_space));
parser.end();
return args;
}
void _ic(Session &session, struct _ic_args const &args)
{
if(!session.current_space)
return;
for(uint32_t address: args.addresses) {
Claim const *claim = session.current_space->disasm.getClaimAt(address);
if(claim)
fmt::print("0x{:08x} is claimed by {}\n", address, claim->str());
else
fmt::print("0x{:08x} is not claimed\n", address);
}
}
//---
// io
//---
static char const *info_str
= "OS: type %s version %02d.%02d.%04d\n"
"\n"
"Header information:\n"
" Bootcode timestamp (DateA) (0x%000008x) : %s\n"
" Bootcode checksum (0x%000008x) : 0x%08x\n"
" Serial number (0x%000008x) : %s\n"
" OS version (0x%000008x) : %s\n";
static char const *footer_str
= "\nFooter information:\n"
" Detected footer address : 0x%08x\n"
" Langdata entries found : %d\n"
" OS date (DateO) (0x%000008x) : %s\n"
" OS checksum (0x%000008x) : 0x%08x\n"
" Computed OS checksum : 0x%08x\n";
static char const *syscall_str
= "\nSyscall information:\n"
" Syscall table address (0x%000008x) : 0x%08x\n"
" Entries that point to valid memory : 0x%x\n"
" First seemingly invalid entry : 0x%08x\n"
" Syscall entries outside ROM:\n";
static char const *syscall_nonrom_str = " %%%04x -> %08x (%s memory)\n";
static std::string parse_io(Session &, Parser &parser)
{
std::string name = parser.at_end() ? "" : parser.symbol("vspace_name");
parser.end();
return name;
}
void _io(Session &session, std::string name)
{
VirtualSpace *space = session.current_space;
if(name != "")
space = session.get_space(name);
if(!space)
return;
OS *os = space->os_analysis();
if(!os)
throw CommandError("os analysis on '{}' failed", name);
printf(info_str, (os->type == OS::FX ? "FX" : "CG"), os->version_major,
os->version_minor, os->version_patch, os->bootcode_timestamp.address,
os->bootcode_timestamp.value.c_str(), os->bootcode_checksum.address,
os->bootcode_checksum, os->serial_number.address,
os->serial_number.value.c_str(), os->version.address,
os->version.value.c_str());
if(os->footer == (uint32_t)-1) {
printf("\nFooter could not be found.\n");
}
else {
printf(footer_str, os->footer, os->langdata, os->timestamp.address,
os->timestamp.value.c_str(), os->checksum.address, os->checksum,
os->computed_checksum);
}
uint32_t syscall_table = os->syscall_table_address();
uint32_t first_noncall
= space->read_u32(syscall_table + 4 * os->syscall_count());
printf(syscall_str, (os->type == OS::FX ? 0x8001007c : 0x8002007c),
syscall_table, os->syscall_count(), first_noncall);
int total = 0;
for(int i = 0; i < os->syscall_count(); i++) {
uint32_t e = os->syscall(i);
MemoryRegion const *r = MemoryRegion::region_for(e);
if(!r || r->name == "ROM" || r->name == "ROM_P2")
continue;
printf(syscall_nonrom_str, i, e, r->name.c_str());
total++;
}
if(!total)
printf(" (none)\n");
}
//---
// isc
//---
struct _isc_args
{
std::string vspace_name;
bool sort = false;
std::vector<uint32_t> addresses;
};
static struct _isc_args parse_isc(Session &session, Parser &parser)
{
_isc_args args;
parser.option("sort",
[&args](std::string const &value) { args.sort = (value == "true"); });
parser.option("vspace",
[&args](std::string const &value) { args.vspace_name = value; });
parser.accept_options();
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);
}
}
while(!parser.at_end())
args.addresses.push_back(parser.expr(space));
parser.end();
return args;
}
struct SyscallInfo
{
uint32_t address;
int id;
};
bool operator<(const SyscallInfo &left, const SyscallInfo &right)
{
return (left.address < right.address) || (left.id < right.id);
}
void _isc(Session &session, std::string vspace_name, bool sort,
std::vector<uint32_t> addresses)
{
VirtualSpace *space = session.current_space;
if(!space) {
FxOS_log(ERR, "no virtual space selected");
return;
}
if(!vspace_name.empty())
space = session.get_space(vspace_name);
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(!addresses.empty()) {
if(sort)
std::sort(&addresses[0], &addresses[addresses.size()]);
for(uint32_t address: addresses) {
int syscall = os->find_syscall(address);
if(syscall == -1)
continue;
fmt::print(theme(3), " 0x{:08x}", address);
fmt::print(theme(10), " %{:04x}", syscall);
fmt::print("\n");
}
return;
}
int total = os->syscall_count();
auto info = std::make_unique<SyscallInfo[]>(total);
for(int i = 0; i < total; i++)
info[i] = (SyscallInfo) {.address = os->syscall(i), .id = i};
if(sort)
std::sort(&info[0], &info[total]);
for(int i = 0; i < total; i++) {
fmt::print(theme(3), " 0x{:08x}", info[i].address);
fmt::print(theme(10), " %{:04x}", info[i].id);
fmt::print("\n");
}
}
//---
// is
//---
struct _is_args
{
std::string vspace_name;
std::optional<FxOS::Symbol> symbol;
bool sort;
};
static struct _is_args parse_is(Session &session, Parser &parser)
{
_is_args args;
parser.option("vspace",
[&args](std::string const &value) { args.vspace_name = value; });
parser.option("sort",
[&args](std::string const &value) { args.sort = (value == "true"); });
parser.accept_options();
FxOS::Symbol s;
if(!parser.at_end()) {
if(parser.lookahead().type == T::SYSCALL) {
s.type = FxOS::Symbol::Syscall;
s.value = parser.expect(T::SYSCALL).value.NUM;
}
else {
s.type = FxOS::Symbol::Address;
s.value = parser.expr(session.current_space);
}
args.symbol = s;
}
else {
args.symbol = {};
}
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.end();
return args;
}
void _is(Session &session, std::string vspace_name,
std::optional<FxOS::Symbol> symbol, bool sort)
{
VirtualSpace *space = session.current_space;
if(!space) {
FxOS_log(ERR, "no virtual space selected");
return;
}
if(!vspace_name.empty())
space = session.get_space(vspace_name);
if(!space) {
FxOS_log(ERR, "virtual space '%s' does not exist", vspace_name);
return;
}
std::vector<FxOS::Symbol> symbols;
if(!symbol.has_value())
symbols = space->symbols.symbols;
else {
FxOS::Symbol s = symbol.value();
std::optional<std::string> name = space->symbols.query(s.type, s.value);
if(name.has_value())
s.name = name.value();
else if(s.type == FxOS::Symbol::Address) {
if(vspace_name.empty())
FxOS_log(ERR,
"no symbol exists for address 0x%08x in current virtual space",
s.value);
else
FxOS_log(ERR,
"no symbol exists for syscall 0x%08x in virtual space '%s'",
s.value, vspace_name);
return;
}
else if(s.type == FxOS::Symbol::Syscall) {
if(vspace_name.empty())
FxOS_log(ERR,
"no symbol exists for syscall %%%04x in current virtual space",
s.value);
else
FxOS_log(ERR,
"no symbol exists for syscall %%%04x in virtual space '%s'",
s.value, vspace_name);
return;
}
symbols = {s};
}
if(sort)
std::sort(&symbols[0], &symbols[symbols.size()]);
for(auto const &s: symbols) {
if(s.type == FxOS::Symbol::Syscall) {
fmt::print(theme(10), " %{:04x}", s.value);
}
else {
fmt::print(" 0x{:08x}", s.value);
}
fmt::print(" {}\n", s.name);
}
}
//---
// Command registration
//---
static ShellCommand _ic_cmd(
"ic", [](Session &s, Parser &p) { _ic(s, parse_ic(s, p)); },
[](Session &s, Parser &p) { parse_ic(s, p); }, "Info Claims", R"(
ic <address>...
Prints information about claims over the specified addresses. Claims are
usually generated by analysis commands and allow sections of the OS to be
marked as part of functions, data, interrupt handlers, etc.
)");
static ShellCommand _io_cmd(
"io", [](Session &s, Parser &p) { _io(s, parse_io(s, p)); },
[](Session &s, Parser &p) { parse_io(s, p); }, "Info OS", R"(
io [<vspace_name>]
Prints information about the OS mapped in the named virtual space (defaults to
the current one). This usually requires an OS binary to be mapped to ROM.
)");
static ShellCommand _isc_cmd(
"isc",
[](Session &s, Parser &p) {
auto args = parse_isc(s, p);
_isc(s, args.vspace_name, args.sort, args.addresses);
},
[](Session &s, Parser &p) { parse_isc(s, p); }, "Info Syscalls", R"(
isc [sort=true] [vspace=<virtual_space>] [<address>...]
Prints the syscall table for the specified virtual space (defaults to the
current one). By default, syscalls are enumerated by syscall number. If
sort=true is specified, they are instead sorted by address.
)");
static ShellCommand _is_cmd(
"is",
[](Session &s, Parser &p) {
auto args = parse_is(s, p);
_is(s, args.vspace_name, args.symbol, args.sort);
},
[](Session &s, Parser &p) { parse_is(s, p); }, "Info Symbols", R"(
is [sort=true] [vspace=<virtual_space>] [<address|syscall>]
Lists symbols in the specified virtual space (defaults to the current
one). By default, all symbols are listed, but if an address or syscall is
provided, the symbol associated with it will be printed instead. If sort=true,
symbols will be sorted by syscall number and then address.
)");