fxos: print (still WIP) analysis results

Looking good.
This commit is contained in:
Lephenixnoir 2023-11-14 01:19:16 +01:00
parent ff2e9c2072
commit c299a5f1b6
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
9 changed files with 159 additions and 36 deletions

View File

@ -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;

View File

@ -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);
});
}

View File

@ -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 */
};

View File

@ -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)

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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>

View File

@ -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);
}
}