fxos/lib/view/assembly.cpp

212 lines
6.4 KiB
C++

//---------------------------------------------------------------------------//
// 1100101 |_ mov #0, r4 __ //
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
// |_ base# + offset |_| /_\_\___/__/ //
//---------------------------------------------------------------------------//
#include <fxos/view/assembly.h>
#include <fxos/binary.h>
#include <fxos/util/format.h>
#include <fxos/util/Queue.h>
#include <vector>
#include <cstdio>
#include <cstring>
#include <fmt/color.h>
namespace FxOS {
/* Output for a single argument, which consists of one or more text segments
each with their own text style. */
using ArgumentOutput = std::vector<std::pair<fmt::text_style, std::string>>;
static inline bool output(ArgumentOutput &out, ViewAssemblyOptions::Promotion p,
fmt::text_style style, std::string str)
{
if(p == ViewAssemblyOptions::Never)
return true;
if(p == ViewAssemblyOptions::Promote)
out.pop_back();
out.push_back({style, std::move(str)});
return false;
}
static void renderArgument(AsmArgument const &arg, Argument const &a,
ArgumentOutput &out, ViewAssemblyOptions const &opts)
{
out.push_back({{}, arg.str()});
// clang-format off
enum { None, PCJump, PCRelative, PCAddr, Location, Constant, SyscallNumber,
ObjectName }
type = None;
// clang-format on
if(arg.kind == AsmArgument::PcJump)
type = PCJump;
else if(arg.kind == AsmArgument::PcRel)
type = PCRelative;
else if(arg.kind == AsmArgument::PcAddr)
type = PCAddr;
if(type == PCJump || type == PCRelative || type == PCAddr) {
auto p = (type == PCJump) ? opts.promotions.PCJump_to_Location
: (type == PCAddr) ? opts.promotions.PCAddr_to_Location
: opts.promotions.PCRelative_to_Location;
if(!RelConstDomain().is_constant(a.location))
return;
if(output(out, p, {}, format("<%s>", a.location.str())))
return;
type = (type == PCRelative) ? Location : Constant;
}
if(type == Location) {
// TODO: Check that this is a read operation!
auto p = opts.promotions.ReadLocation_to_Constant;
if(!a.value || output(out, p, {}, a.value.str()))
return;
type = Constant;
}
/* Promote to object name first if available... */
if(type == Constant && a.value && opts.binary) {
auto p = opts.promotions.Constant_to_ObjectName;
u32 address = RelConstDomain().constant_value(a.value);
BinaryObject *obj = opts.binary->objectAt(address);
if(obj) {
if(output(out, p, {}, obj->name()))
return;
type = ObjectName;
}
}
/* ... or, as a default, a syscall number */
if(type == Constant && a.value && a.syscall_id >= 0) {
auto p = opts.promotions.Constant_to_SyscallNumber;
if(output(out, p, {}, format("%%%04x", a.syscall_id)))
return;
type = SyscallNumber;
}
}
//=== Legacy-style instruction printer ===//
static void doOldInst(u32 pc, OldInstruction &i,
ViewAssemblyOptions const &opts, u32 &m_lastAddress)
{
OS *os = opts.binary ? opts.binary->OSAnalysis() : nullptr;
ArgumentOutput argout;
/* Ellipsis if there is a gap since last instruction */
if(m_lastAddress + 1 != 0 && pc != m_lastAddress + 2)
printf(" ...\n");
/* Preliminary syscall number */
int syscall_id;
if(os && (syscall_id = os->find_syscall(pc)) >= 0) {
printf("\n<%%%04x", syscall_id);
BinaryObject *obj = opts.binary ? opts.binary->objectAt(pc) : nullptr;
if(obj)
printf(" %s", obj->name().c_str());
printf(">\n");
}
/* Raw data if instruction cannot be decoded */
printf(" %08x: %04x", pc, (i.inst ? i.inst->opcode : i.opcode));
if(!i.inst) {
printf("\n");
m_lastAddress = pc;
return;
}
/* Mnemonic */
static char const *suffixes[5] = {"", ".b", ".w", "", ".l"};
char const *suffix = suffixes[(i.inst->opsize <= 4) ? i.inst->opsize : 0];
int spacing
= i.inst->arg_count ? 8 - strlen(i.inst->mnemonic) - strlen(suffix) : 0;
printf(" %s%s%*s", i.inst->mnemonic, suffix, spacing, "");
/* Arguments */
for(size_t n = 0; n < i.inst->arg_count; n++) {
if(n)
printf(", ");
renderArgument(i.inst->args[n], i.args[n], argout, opts);
for(size_t i = 0; i < argout.size(); i++) {
if(i != 0)
printf(" ");
printf("%s", argout[i].second.c_str());
}
argout.clear();
}
printf("\n");
m_lastAddress = pc;
}
void viewAssemblyLegacyRegion(
Binary &binary, MemoryRegion r, ViewAssemblyOptions *opts_ptr)
{
ViewAssemblyOptions opts;
if(opts_ptr)
opts = *opts_ptr;
opts.binary = &binary;
u32 lastAddress = 0xffffffff;
for(u32 pc = r.start & -2; pc <= r.end; pc += 2) {
OldInstruction *i = binary.vspace().disasm.getInstructionAt(pc, true);
if(i != nullptr)
doOldInst(pc, *i, opts, lastAddress);
}
}
void viewAssemblyLegacyAddress(
Binary &binary, u32 pc, ViewAssemblyOptions *opts_ptr)
{
ViewAssemblyOptions opts;
if(opts_ptr)
opts = *opts_ptr;
opts.binary = &binary;
u32 lastAddress = 0xffffffff;
Queue<u32> queue;
queue.enqueue(pc);
while(!queue.empty()) {
u32 pc = queue.pop();
OldInstruction *i = binary.vspace().disasm.getInstructionAt(pc, true);
if(i == nullptr)
continue;
/* Enqueue successors */
if(!i->terminal && !i->jump)
queue.enqueue(pc + 2);
if(i->jump || i->condjump)
queue.enqueue(i->jmptarget);
}
/* Print explored instructions in increasing order of addresses */
for(u32 pc: queue.seen) {
OldInstruction *i = binary.vspace().disasm.getInstructionAt(pc, false);
if(i)
doOldInst(pc, *i, opts, lastAddress);
}
}
//=== Binary-API assembly printer ===//
void viewAssemblyInstruction(
Instruction const &inst, ViewAssemblyOptions *opts);
void viewAssemblyBasicBlock(BasicBlock const &bb, ViewAssemblyOptions *opts);
void viewAssemblyFunction(Function const &fun, ViewAssemblyOptions *opts);
} /* namespace FxOS */