212 lines
4.6 KiB
C++
212 lines
4.6 KiB
C++
//---
|
|
// fxos.passes.print: Print disassembly
|
|
//---
|
|
|
|
#include <fxos/disasm-passes/print.h>
|
|
#include <fxos/disassembly.h>
|
|
|
|
#include <cstdarg>
|
|
#include <cstring>
|
|
|
|
namespace FxOS {
|
|
|
|
PrintPass::PrintPass(Disassembly &disasm,
|
|
std::vector<SymbolTable> const &symtables):
|
|
InstructionDisassemblyPass(disasm, "print"), m_symtables(symtables),
|
|
m_last_address(0xffffffff)
|
|
{
|
|
/* Default parameters: all 0 */
|
|
|
|
/* Use an OS observer to describe syscalls in header lines */
|
|
Target &t = disasm.target();
|
|
try {
|
|
m_os = std::make_unique<OS>(t);
|
|
}
|
|
catch(std::exception &) {}
|
|
}
|
|
|
|
void PrintPass::analyze(uint32_t pc, ConcreteInstruction &ci)
|
|
{
|
|
Instruction const *i = ci.inst;
|
|
|
|
/* Ellipsis if there is a gap since last instruction */
|
|
|
|
if(m_last_address+1 != 0 && pc != m_last_address+2)
|
|
printf(" ...\n");
|
|
|
|
/* Preliminary syscall number */
|
|
|
|
int syscall_id;
|
|
if(m_os && (syscall_id = m_os->find_syscall(pc)) >= 0)
|
|
{
|
|
printf("\n<%%%03x", syscall_id);
|
|
|
|
auto maybe_str = symquery(Symbol::Syscall, syscall_id);
|
|
if(maybe_str) printf(" %s", (*maybe_str).c_str());
|
|
|
|
printf(">\n");
|
|
}
|
|
|
|
/* Raw data if instruction cannot be decoded */
|
|
|
|
printf(" %08x: %04x", pc, (i ? i->opcode : ci.opcode));
|
|
if(!i)
|
|
{
|
|
printf("\n");
|
|
m_last_address = pc;
|
|
return;
|
|
}
|
|
|
|
/* Mnemonic */
|
|
|
|
static char const *suffixes[5] = { "", ".b", ".w", "", ".l" };
|
|
char const *suffix = suffixes[(i->opsize <= 4) ? i->opsize : 0];
|
|
|
|
int spacing = i->arg_count ? 8 - strlen(i->mnemonic) - strlen(suffix) : 0;
|
|
printf(" %s%s%*s", i->mnemonic, suffix, spacing, "");
|
|
|
|
/* Arguments */
|
|
|
|
for(size_t n = 0; n < i->arg_count; n++)
|
|
{
|
|
Argument const &a = i->args[n];
|
|
ConcreteInstructionArg const &arg = ci.args[n];
|
|
|
|
if(n) printf(", ");
|
|
|
|
if(a.kind == Argument::PcJump)
|
|
{
|
|
queue(a.str());
|
|
pcjumploc(arg);
|
|
queue_flush();
|
|
}
|
|
else if(a.kind == Argument::PcRel)
|
|
{
|
|
queue(a.str());
|
|
pcrelloc(arg);
|
|
queue_flush();
|
|
}
|
|
else if(a.kind == Argument::PcAddr)
|
|
{
|
|
queue(a.str());
|
|
pcaddrloc(arg);
|
|
queue_flush();
|
|
}
|
|
else
|
|
{
|
|
queue(a.str());
|
|
queue_flush();
|
|
}
|
|
}
|
|
|
|
printf("\n");
|
|
m_last_address = pc;
|
|
}
|
|
|
|
std::optional<std::string> PrintPass::symquery(Symbol::Type type,
|
|
uint32_t value)
|
|
{
|
|
for(int i = m_symtables.size() - 1; i >= 0; i--)
|
|
{
|
|
SymbolTable const &st = m_symtables[i];
|
|
if(!st.is_usable_on(m_disasm.target())) continue;
|
|
if(m_os && !st.is_usable_on(*m_os)) continue;
|
|
|
|
auto maybe_str = st.query(type, value);
|
|
if(maybe_str) return maybe_str;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
void PrintPass::queue(std::string str, bool override)
|
|
{
|
|
if(override && m_messages.size())
|
|
m_messages.pop_back();
|
|
|
|
m_messages.push_back(str);
|
|
}
|
|
|
|
void PrintPass::queue_flush()
|
|
{
|
|
for(size_t i = 0; i < m_messages.size(); i++)
|
|
{
|
|
if(i != 0) printf(" ");
|
|
printf("%s", m_messages[i].c_str());
|
|
}
|
|
|
|
m_messages.clear();
|
|
}
|
|
|
|
void PrintPass::pcjumploc(ConcreteInstructionArg const &arg)
|
|
{
|
|
if(!RelConstDomain().is_constant(arg.location)) return;
|
|
if(promote_pcjump_loc == Never) return;
|
|
|
|
queue(format("<%s>", arg.location.str()), promote_pcjump_loc==Promote);
|
|
syscall(arg);
|
|
}
|
|
|
|
void PrintPass::pcrelloc(ConcreteInstructionArg const &arg)
|
|
{
|
|
if(!RelConstDomain().is_constant(arg.location)) return;
|
|
if(promote_pcrel_loc == Never) return;
|
|
|
|
queue(format("<%s>", arg.location.str()), promote_pcrel_loc==Promote);
|
|
pcrelval(arg);
|
|
}
|
|
|
|
void PrintPass::pcrelval(ConcreteInstructionArg const &arg)
|
|
{
|
|
if(!arg.value || arg.value.type->kind() != DataType::Integer) return;
|
|
if(promote_pcrel_value == Never) return;
|
|
|
|
queue(arg.value.str(), promote_pcrel_value==Promote);
|
|
syscall(arg);
|
|
}
|
|
|
|
void PrintPass::syscall(ConcreteInstructionArg const &arg)
|
|
{
|
|
if(!arg.value || arg.value.type->kind() != DataType::Integer) return;
|
|
|
|
/* If this is not a syscall, try to display as a symbol instead */
|
|
if(promote_syscall == Never || arg.syscall_id < 0)
|
|
{
|
|
symbol(arg);
|
|
return;
|
|
}
|
|
|
|
queue(format("%%%03x", arg.syscall_id), promote_syscall==Promote);
|
|
syscallname(arg);
|
|
}
|
|
|
|
void PrintPass::syscallname(ConcreteInstructionArg const &arg)
|
|
{
|
|
if(arg.syscall_id < 0) return;
|
|
|
|
auto maybe_name = symquery(Symbol::Syscall, arg.syscall_id);
|
|
if(!maybe_name) return;
|
|
|
|
queue(*maybe_name, promote_syscallname==Promote);
|
|
}
|
|
|
|
void PrintPass::symbol(ConcreteInstructionArg const &arg)
|
|
{
|
|
if(!arg.value || arg.value.type->kind() != DataType::Integer) return;
|
|
|
|
auto maybe_name = symquery(Symbol::Address, arg.value.uinteger());
|
|
if(!maybe_name) return;
|
|
|
|
queue(*maybe_name, promote_symbol==Promote);
|
|
}
|
|
|
|
void PrintPass::pcaddrloc(ConcreteInstructionArg const &arg)
|
|
{
|
|
if(!RelConstDomain().is_constant(arg.location)) return;
|
|
if(promote_pcaddr_loc == Never) return;
|
|
|
|
queue(format("<%s>", arg.location.str()), promote_pcaddr_loc==Promote);
|
|
}
|
|
|
|
} /* namespace FxOS */
|