fxos/shell/d.cpp

157 lines
4.0 KiB
C++

#include "commands.h"
#include "errors.h"
#include "parser.h"
#include "shell.h"
#include <fmt/core.h>
#include <fxos/disassembly.h>
#include <fxos/passes/cfg.h>
#include <fxos/passes/pcrel.h>
#include <fxos/passes/syscall.h>
#include <fxos/view/assembly.h>
#include <fxos/function.h>
#include <fxos/util/Timer.h>
#include <fxos/util/log.h>
static void disassemble(
Binary &binary, std::vector<std::string> const &passes, u32 address)
{
for(auto pass: passes) {
Timer timer;
timer.start();
bool ok;
if(pass == "cfg") {
CfgPass p(binary);
ok = p.analyzeAnonymousFunction(address);
}
else if(pass == "pcrel") {
PcrelPass p(binary);
ok = p.analyzeAllInstructions();
}
else if(pass == "syscall") {
OS *os = binary.OSAnalysis();
if(os) {
SyscallPass p(binary, os);
ok = p.analyzeAllInstructions();
}
}
else if(pass == "print" && address + 1) {
// viewAssemblyLegacyAddress(binary, address);
}
else {
FxOS_log(ERR, "unknown pass <%s>", pass);
ok = false;
}
timer.stop();
FxOS_log(LOG, "Finished pass <%s> in %s", pass, timer.format_time());
if(!ok) {
FxOS_log(ERR, "pass <%s> failed", pass);
break;
}
}
if(address + 1) {
Function f(binary, address);
f.exploreFunctionAt(address);
ViewAssemblyOptions opts;
opts.binary = &binary;
viewAssemblyFunction(f, &opts);
}
}
//---
// d
//---
struct _d_args
{
std::variant<long, Range> location;
};
static _d_args parse_d(Session &session, Parser &parser)
{
_d_args args;
if(!session.currentBinary())
return {};
args.location = parser.expr_or_range(session.currentBinary());
parser.end();
return args;
}
void _d(Session &session, std::variant<long, Range> location)
{
Binary *b = session.currentBinary();
if(!b)
return FxOS_log(ERR, "No current binary!\n");
if(std::holds_alternative<Range>(location)) {
Range range = std::get<Range>(location);
if(range.start & 1) {
fmt::print("address 0x{:08x} is odd, starting at 0x{:08x}\n",
range.start, range.start + 1);
range.start++;
}
if(range.end & 1) {
fmt::print("address 0x{:08x} is odd, ending at 0x{:08x}\n",
range.end, range.end - 1);
range.end--;
}
if(range.start >= range.end)
return;
/* Load the block into memory */
for(uint32_t pc = range.start; pc < range.end; pc += 2)
b->vspace().disasm.getInstructionAt(pc, true);
disassemble(*b, {"pcrel", /*"constprop",*/ "syscall"}, -1);
MemoryRegion r;
r.start = range.start;
r.end = range.end - 1;
viewAssemblyLegacyRegion(*b, r);
}
else {
uint32_t address = std::get<long>(location);
if(address & 1) {
fmt::print("address 0x{:08x} is odd, starting at 0x{:08x}\n",
address, address + 1);
address++;
}
/* cfg implicitly does pcrel */
disassemble(*b, {"cfg", /*"constprop",*/ "syscall", "print"}, address);
}
}
//---
// Command registration
//---
static ShellCommand _d_cmd(
"d",
[](Session &s, Parser &p) {
auto args = parse_d(s, p);
_d(s, args.location);
},
[](Session &s, Parser &p) { parse_d(s, p); }, "Disassemble", R"(
d [<address|range>]
Disassembles code starting at the specified address, exploring branches until
function terminators, invalid instructions, or dynamically-computed jumps. The
default address is $ (the cursor of the current virtual space).
The following disassembler passes are run:
cfg Explores the code reachable from the start address
pcrel Computes PC-relative addresses (eg mov.l, mova, bf, bra...)
syscall Annotates uses of syscall table entries with the syscall number
)");