parent
ff2e9c2072
commit
c299a5f1b6
|
@ -49,6 +49,8 @@ struct ProgramState
|
|||
/* Lattice order. */
|
||||
bool le(ProgramState const &other) const;
|
||||
|
||||
std::string str(int indent = 0) const;
|
||||
|
||||
private:
|
||||
/* Values for registers r0..r15 */
|
||||
RelConst m_regs[16];
|
||||
|
@ -94,6 +96,8 @@ struct ProgramStateDiff
|
|||
m_target = static_cast<int>(Target::Unknown);
|
||||
}
|
||||
|
||||
std::string str() const;
|
||||
|
||||
private:
|
||||
int m_target;
|
||||
RelConst m_value;
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace FxOS {
|
|||
class Function;
|
||||
class BasicBlock;
|
||||
class Instruction;
|
||||
class StaticFunctionAnalysis;
|
||||
|
||||
// TODO: move this extern declaration of FxOS::insmap
|
||||
extern std::array<std::optional<AsmInstruction>, 65536> insmap;
|
||||
|
@ -92,6 +93,11 @@ struct Function: public BinaryObject
|
|||
{
|
||||
return m_analysisVersion;
|
||||
}
|
||||
/* Get analysis results if there are any. */
|
||||
StaticFunctionAnalysis const *getAnalysis() const
|
||||
{
|
||||
return m_analysisResult.get();
|
||||
}
|
||||
|
||||
/* Construction functions to be used only by the analysis pass. */
|
||||
bool exploreFunctionAt(u32 address);
|
||||
|
@ -100,11 +106,16 @@ struct Function: public BinaryObject
|
|||
void updateFunctionSize();
|
||||
void setAnalysisVersion(int version);
|
||||
|
||||
/* Analysis execution functions (also semi-private). */
|
||||
void runAnalysis();
|
||||
|
||||
private:
|
||||
/* List of basic blocks (entry block is always number 0) */
|
||||
std::vector<BasicBlock> m_blocks;
|
||||
/* Analysis version */
|
||||
int m_analysisVersion = 0;
|
||||
/* Analysis result */
|
||||
std::unique_ptr<StaticFunctionAnalysis> m_analysisResult;
|
||||
/* ID of the entry block */
|
||||
int m_entryBlockIndex;
|
||||
};
|
||||
|
@ -293,7 +304,7 @@ struct BasicBlock
|
|||
auto successors() // -> [BasicBlock &]
|
||||
{
|
||||
return std::views::all(m_successors)
|
||||
| std::views::transform([this](int index) {
|
||||
| std::views::transform([this](int index) -> BasicBlock & {
|
||||
return parentFunction().basicBlockByIndex(index);
|
||||
});
|
||||
}
|
||||
|
@ -317,7 +328,7 @@ struct BasicBlock
|
|||
auto predecessors() // -> [BasicBlock &]
|
||||
{
|
||||
return std::views::all(m_predecessors)
|
||||
| std::views::transform([this](int index) {
|
||||
| std::views::transform([this](int index) -> BasicBlock & {
|
||||
return parentFunction().basicBlockByIndex(index);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ struct Binary;
|
|||
struct Function;
|
||||
struct BasicBlock;
|
||||
struct Instruction;
|
||||
struct StaticFunctionAnalysis;
|
||||
|
||||
struct ViewAssemblyOptions
|
||||
{
|
||||
|
@ -81,6 +82,12 @@ struct ViewAssemblyOptions
|
|||
/* Binary to get symbols from */
|
||||
Binary *binary = nullptr;
|
||||
|
||||
/* Whether to print basic block details, ie. edges and flags */
|
||||
bool basicBlockDetails = false;
|
||||
|
||||
/* Whether to print function analysis results from the binary */
|
||||
bool printFunctionAnalysis = false;
|
||||
|
||||
/* TODO: More view assembly options, including CFG layout */
|
||||
/* TODO: View assembly options: syntax highlighting */
|
||||
};
|
||||
|
|
|
@ -320,9 +320,9 @@ std::string RelConst::str() const noexcept
|
|||
if(!base && !uval)
|
||||
return "0";
|
||||
if(spe == Bottom)
|
||||
return "Bottom";
|
||||
return "⊥";
|
||||
if(spe == Top)
|
||||
return "Top";
|
||||
return "⊤";
|
||||
|
||||
std::string str;
|
||||
if(arg)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//---------------------------------------------------------------------------//
|
||||
|
||||
#include <fxos/analysis.h>
|
||||
#include <fmt/core.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace FxOS {
|
||||
|
@ -61,6 +62,36 @@ bool ProgramState::le(ProgramState const &other) const
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string ProgramState::str(int indentLength) const
|
||||
{
|
||||
std::string indent(indentLength, ' ');
|
||||
std::string str;
|
||||
|
||||
/* Registers */
|
||||
for(int i = 0; i < 16; i++) {
|
||||
if(i % 4 == 0) {
|
||||
str += (i > 0 ? "\n" : "");
|
||||
str += indent;
|
||||
}
|
||||
else
|
||||
str += " ";
|
||||
|
||||
str += fmt::format("r{}:{}", i, m_regs[i].str());
|
||||
}
|
||||
|
||||
return str + "\n";
|
||||
}
|
||||
|
||||
std::string ProgramStateDiff::str() const
|
||||
{
|
||||
if(m_target == static_cast<int>(Target::None))
|
||||
return "()";
|
||||
if(m_target == static_cast<int>(Target::Unknown))
|
||||
return "⊤";
|
||||
|
||||
return fmt::format("r{} ← {}", m_target, m_value.str());
|
||||
}
|
||||
|
||||
/* Information stored for each block during the fixpoint iteration */
|
||||
struct BlockStates
|
||||
{
|
||||
|
@ -73,6 +104,19 @@ struct BlockStates
|
|||
static ProgramStateDiff interpretInstruction(
|
||||
Instruction const &ins, ProgramState const &PS)
|
||||
{
|
||||
RelConstDomain RCD;
|
||||
ProgramStateDiff diff;
|
||||
diff.setUnknown();
|
||||
|
||||
// TODO: Do this properly
|
||||
u16 opc = ins.opcode().opcode;
|
||||
if((opc & 0xf000) == 0xe000) {
|
||||
int reg = (opc >> 8) & 0xf;
|
||||
int val = (int8_t)opc;
|
||||
diff.setRegisterUpdate(reg, RCD.constant(val));
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
static void interpretBlock(BasicBlock const &bb, BlockStates &states)
|
||||
|
@ -114,8 +158,8 @@ std::unique_ptr<StaticFunctionAnalysis> analyzeFunction(Function const &f)
|
|||
BasicBlock const &bb = f.basicBlockByIndex(i);
|
||||
VBS[i].nextEntry.setBottom();
|
||||
|
||||
for(auto succ: bb.successors())
|
||||
VBS[i].nextEntry.joinWith(VBS[succ.blockIndex()].exit);
|
||||
for(int succIndex: bb.successorsByIndex())
|
||||
VBS[i].nextEntry.joinWith(VBS[succIndex].exit);
|
||||
}
|
||||
|
||||
/* Determine whether a fixpoint has been reached yet */
|
||||
|
@ -141,6 +185,4 @@ std::unique_ptr<StaticFunctionAnalysis> analyzeFunction(Function const &f)
|
|||
return an;
|
||||
}
|
||||
|
||||
// TODO_need_bb_successors;
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//---------------------------------------------------------------------------//
|
||||
|
||||
#include <fxos/function.h>
|
||||
#include <fxos/analysis.h>
|
||||
#include <fxos/util/format.h>
|
||||
#include <fxos/util/log.h>
|
||||
|
||||
|
@ -14,7 +15,8 @@ namespace FxOS {
|
|||
//=== Function ===//
|
||||
|
||||
Function::Function(Binary &binary, u32 address):
|
||||
BinaryObject(binary, BinaryObject::Function, address, 0)
|
||||
BinaryObject(binary, BinaryObject::Function, address, 0),
|
||||
m_analysisResult {nullptr}
|
||||
{
|
||||
/* Size is not determined at first. */
|
||||
|
||||
|
@ -68,6 +70,12 @@ void Function::sortBasicBlocks()
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update instruction's parent block numbers */
|
||||
for(uint i = 0; i < m_blocks.size(); i++) {
|
||||
for(Instruction &ins: m_blocks[i])
|
||||
ins.setBlockContext(i, ins.indexInBlock());
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the function's BinaryObject size by finding the last address covered
|
||||
|
@ -91,6 +99,11 @@ void Function::setAnalysisVersion(int version)
|
|||
m_analysisVersion = version;
|
||||
}
|
||||
|
||||
void Function::runAnalysis()
|
||||
{
|
||||
m_analysisResult = analyzeFunction(*this);
|
||||
}
|
||||
|
||||
/* The first step in building function CFGs is delimiting the blocks. Starting
|
||||
from the entry point, we generate "superblocks" by reading instructions
|
||||
linearly until we find a terminator.
|
||||
|
@ -272,20 +285,25 @@ bool Function::exploreFunctionAt(u32 functionAddress)
|
|||
else {
|
||||
successorAddresses[pc].push_back(*it);
|
||||
}
|
||||
|
||||
// TODO: Set successors
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort blocks now before creating CFG nodes, which are index-based */
|
||||
sortBasicBlocks();
|
||||
|
||||
/* Set block predecessors */
|
||||
for(auto &[pc, succ]: successorAddresses) {
|
||||
BasicBlock &bb = basicBlockByAddress(pc);
|
||||
for(u32 a: succ)
|
||||
bb.addSuccessor(&basicBlockByAddress(a));
|
||||
}
|
||||
|
||||
// TODO: Set predecessors
|
||||
/* Set block predecessors */
|
||||
for(BasicBlock &bb: *this) {
|
||||
for(BasicBlock &succ: bb.successors()) {
|
||||
succ.addPredecessor(&bb);
|
||||
}
|
||||
}
|
||||
|
||||
updateFunctionSize();
|
||||
return true;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//---------------------------------------------------------------------------//
|
||||
|
||||
#include <fxos/view/assembly.h>
|
||||
#include <fxos/analysis.h>
|
||||
#include <fxos/binary.h>
|
||||
#include <fxos/function.h>
|
||||
#include <fxos/util/format.h>
|
||||
|
@ -53,7 +54,7 @@ static void renderArgument(AsmArgument const &arg, u32 pc, int opsize,
|
|||
else if(arg.kind == AsmArgument::PcAddr)
|
||||
type = PCAddr;
|
||||
|
||||
u32 value;
|
||||
u32 value = 0;
|
||||
bool hasValue = false;
|
||||
OS *os = opts.binary ? opts.binary->OSAnalysis() : nullptr;
|
||||
VirtualSpace *vspace = opts.binary ? &opts.binary->vspace() : nullptr;
|
||||
|
@ -246,25 +247,42 @@ void viewAssemblyInstruction(Instruction const &ins, ViewAssemblyOptions *opts)
|
|||
|
||||
int spacing
|
||||
= opcode.arg_count ? 8 - strlen(opcode.mnemonic) - strlen(suffix) : 0;
|
||||
printf(" %s%s%*s", opcode.mnemonic, suffix, spacing, "");
|
||||
std::string str = " ";
|
||||
str += opcode.mnemonic;
|
||||
str += suffix;
|
||||
str += std::string(spacing, ' ');
|
||||
|
||||
/* Arguments */
|
||||
for(size_t n = 0; n < opcode.arg_count; n++) {
|
||||
if(n)
|
||||
printf(", ");
|
||||
str += ", ";
|
||||
|
||||
renderArgument(opcode.args[n], pc, opcode.opsize, argout, *opts);
|
||||
|
||||
for(size_t i = 0; i < argout.size(); i++) {
|
||||
if(i != 0)
|
||||
printf(" ");
|
||||
printf("%s", argout[i].second.c_str());
|
||||
str += " ";
|
||||
str += argout[i].second;
|
||||
}
|
||||
|
||||
argout.clear();
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
if(opts->printFunctionAnalysis) {
|
||||
auto *an = ins.parentFunction().getAnalysis();
|
||||
|
||||
if(an) {
|
||||
auto &block = an->blocks[ins.parentBlock().blockIndex()];
|
||||
ProgramStateDiff const &diff = block.diffs[ins.indexInBlock()];
|
||||
|
||||
if(str.size() < 32)
|
||||
str += std::string(32 - str.size(), ' ');
|
||||
str += "# ";
|
||||
str += diff.str();
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("{}\n", str);
|
||||
}
|
||||
|
||||
static std::string objectsAt(
|
||||
|
@ -310,25 +328,43 @@ void viewAssemblyBasicBlock(BasicBlock const &bb, ViewAssemblyOptions *opts)
|
|||
}
|
||||
printf(":\n");
|
||||
|
||||
printf(" Successors:");
|
||||
for(u32 succ: bb.successorsByAddress())
|
||||
printf(" bb.%08x", succ);
|
||||
if(bb.successorCount() == 0)
|
||||
printf(" (none)");
|
||||
printf("\n");
|
||||
if(opts->basicBlockDetails) {
|
||||
printf(" Successors:");
|
||||
for(u32 succ: bb.successorsByAddress())
|
||||
printf(" bb.%08x", succ);
|
||||
if(bb.successorCount() == 0)
|
||||
printf(" (none)");
|
||||
printf("\n");
|
||||
|
||||
printf(" Flags:");
|
||||
if(bb.isEntryBlock())
|
||||
printf(" IsEntryBlock");
|
||||
if(bb.isTerminator())
|
||||
printf(" IsTerminator");
|
||||
if(bb.hasDelaySlot())
|
||||
printf(" HasDelaySlot");
|
||||
if(bb.hasNoTerminator())
|
||||
printf(" NoTerminator");
|
||||
if(!(bb.getFlags() & BasicBlock::ValidFlags))
|
||||
printf(" (none)");
|
||||
printf("\n");
|
||||
printf(" Predecessors:");
|
||||
for(u32 succ: bb.predecessorsByAddress())
|
||||
printf(" bb.%08x", succ);
|
||||
if(bb.predecessorCount() == 0)
|
||||
printf(" (none)");
|
||||
printf("\n");
|
||||
|
||||
printf(" Flags:");
|
||||
if(bb.isEntryBlock())
|
||||
printf(" IsEntryBlock");
|
||||
if(bb.isTerminator())
|
||||
printf(" IsTerminator");
|
||||
if(bb.hasDelaySlot())
|
||||
printf(" HasDelaySlot");
|
||||
if(bb.hasNoTerminator())
|
||||
printf(" NoTerminator");
|
||||
if(!(bb.getFlags() & BasicBlock::ValidFlags))
|
||||
printf(" (none)");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if(opts->printFunctionAnalysis) {
|
||||
auto *an = bb.parentFunction().getAnalysis();
|
||||
if(an) {
|
||||
auto &block = an->blocks[bb.blockIndex()];
|
||||
printf(" Entry state:\n");
|
||||
fmt::print("{}", block.entry.str(6));
|
||||
}
|
||||
}
|
||||
|
||||
for(Instruction const &ins: bb)
|
||||
viewAssemblyInstruction(ins, opts);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "util.h"
|
||||
|
||||
#include <fxos/function.h>
|
||||
#include <fxos/analysis.h>
|
||||
#include <fxos/vspace.h>
|
||||
#include <fxos/util/Timer.h>
|
||||
#include <fxos/util/log.h>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <fxos/passes/syscall.h>
|
||||
#include <fxos/view/assembly.h>
|
||||
#include <fxos/function.h>
|
||||
#include <fxos/analysis.h>
|
||||
#include <fxos/util/Timer.h>
|
||||
#include <fxos/util/log.h>
|
||||
|
||||
|
@ -85,8 +86,11 @@ void _d(Session &session, std::variant<long, Range> location)
|
|||
|
||||
Function f(*b, address);
|
||||
if(f.exploreFunctionAt(address)) {
|
||||
f.runAnalysis();
|
||||
|
||||
ViewAssemblyOptions opts;
|
||||
opts.binary = b;
|
||||
opts.printFunctionAnalysis = true;
|
||||
viewAssemblyFunction(f, &opts);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue