153 lines
3.6 KiB
C++
153 lines
3.6 KiB
C++
#include "fxos-cli.h"
|
|
#include <fxos/disassembly.h>
|
|
#include <fxos/memory.h>
|
|
#include <fxos/target.h>
|
|
#include <fxos/util.h>
|
|
#include <fxos/log.h>
|
|
#include <fxos/os.h>
|
|
|
|
#include <fxos/disasm-passes/cfg.h>
|
|
#include <fxos/disasm-passes/pcrel.h>
|
|
#include <fxos/disasm-passes/syscall.h>
|
|
#include <fxos/disasm-passes/print.h>
|
|
|
|
using namespace FxOS;
|
|
using namespace FxOS::Log;
|
|
|
|
int disassembly(Library &library, Target &target, char const *ref,
|
|
std::vector<std::string> passes)
|
|
{
|
|
Disassembly disasm(target);
|
|
int len=0;
|
|
|
|
/* Observe the target only if it has an OS mapped */
|
|
std::unique_ptr<OS> os;
|
|
if(target.covers(MemoryRegion::ROM)) os = std::make_unique<OS>(target);
|
|
|
|
/* Parameters inside the ref */
|
|
uint32_t address = -1;
|
|
uint32_t blocklen;
|
|
int syscall_id;
|
|
char blockmul[2] = { 0 };
|
|
|
|
enum { RefNone=0, RefSyscall, RefAddress, RefBlock } reftype = RefNone;
|
|
|
|
/* Parse different flavors of references. %<hexa>: syscall */
|
|
if(sscanf(ref, "%%%x", &syscall_id) == 1)
|
|
reftype = RefSyscall;
|
|
/* Pure hexa: address */
|
|
else if(sscanf(ref, "%x%n", &address, &len) == 1 && !ref[len])
|
|
reftype = RefAddress;
|
|
/* Hexa with a size: a block */
|
|
else if(sscanf(ref,"%x:%x%n",&address,&blocklen,&len)==2 && !ref[len])
|
|
reftype = RefBlock;
|
|
/* Variant of block size that includes a letter k/M/G (note that the
|
|
"%[]" specifier never matches empty strings */
|
|
else if(sscanf(ref, "%x:%x%1[kMG]%n", &address, &blocklen, blockmul,
|
|
&len) == 3 && !ref[len])
|
|
{
|
|
reftype = RefBlock;
|
|
|
|
int mul = blockmul[0];
|
|
if(mul == 'k') blocklen <<= 10;
|
|
if(mul == 'M') blocklen <<= 20;
|
|
if(mul == 'G') blocklen <<= 30;
|
|
}
|
|
/* Anything else: look up symbols */
|
|
else
|
|
{
|
|
std::string name = ref;
|
|
for(auto const &symtable: library.sym_tables())
|
|
{
|
|
std::optional<Symbol> sym = symtable.lookup(name);
|
|
if(!sym) continue;
|
|
|
|
if(sym->type == Symbol::Syscall)
|
|
reftype = RefSyscall, syscall_id = sym->value;
|
|
if(sym->type == Symbol::Address)
|
|
reftype = RefAddress, address = sym->value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Now try to load the address for this reference */
|
|
if(reftype == RefSyscall)
|
|
{
|
|
if(!os)
|
|
{
|
|
log(ERR "cannot disassemble syscall %s: target does "
|
|
"not have an OS mapped", ref);
|
|
return 1;
|
|
}
|
|
if(syscall_id >= os->syscall_count())
|
|
{
|
|
log(ERR "this OS only has %#x syscalls",
|
|
os->syscall_count());
|
|
return 1;
|
|
}
|
|
|
|
address = os->syscall(syscall_id);
|
|
}
|
|
if(reftype == RefAddress || reftype == RefBlock)
|
|
{
|
|
if(address & 1)
|
|
{
|
|
log(WRN "address %08x is odd, will start at %08x",
|
|
address, address+1);
|
|
address++;
|
|
}
|
|
}
|
|
if(reftype == RefBlock)
|
|
{
|
|
/* Load the block into memory */
|
|
for(uint32_t pc = address; pc < address + blocklen; pc += 2)
|
|
{
|
|
disasm.readins(pc);
|
|
}
|
|
}
|
|
if(reftype == RefNone)
|
|
{
|
|
log(ERR "cannot interpret '%s' (not a syscall id, not an "
|
|
"address, and no such symbol in library)", ref);
|
|
return 1;
|
|
}
|
|
|
|
for(auto pass: passes)
|
|
{
|
|
auto start = timer_start();
|
|
log(LOG "Running pass %s...\\", pass);
|
|
|
|
if(pass == "cfg" && reftype != RefBlock)
|
|
{
|
|
CfgPass p(disasm);
|
|
p.run(address);
|
|
}
|
|
else if(pass == "pcrel")
|
|
{
|
|
PcrelPass p(disasm);
|
|
p.run();
|
|
}
|
|
else if(pass == "syscall")
|
|
{
|
|
SyscallPass p(disasm, os.get());
|
|
p.run();
|
|
}
|
|
else if(pass == "print")
|
|
{
|
|
PrintPass p(disasm, library.sym_tables());
|
|
|
|
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.run();
|
|
}
|
|
log(LOG "%s", timer_format(timer_end(start)));
|
|
}
|
|
|
|
return 0;
|
|
}
|