From 944745d0e3318ecb4e8474e308017f9dcba78f42 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Wed, 29 Nov 2023 17:33:22 +0100 Subject: [PATCH] fxos: print call targets in [d -a] --- include/fxos/function.h | 27 +++++++++-- include/fxos/view/assembly.h | 10 +++- lib/analysis.cpp | 8 ++-- lib/function.cpp | 18 +++++++ lib/view/assembly.cpp | 91 ++++++++++++++++++++++-------------- shell/d.cpp | 27 +++++++---- 6 files changed, 126 insertions(+), 55 deletions(-) diff --git a/include/fxos/function.h b/include/fxos/function.h index 697bcae..c8a6c7c 100644 --- a/include/fxos/function.h +++ b/include/fxos/function.h @@ -19,10 +19,12 @@ namespace FxOS { -class Function; -class BasicBlock; -class Instruction; -class StaticFunctionAnalysis; +struct Function; +struct BasicBlock; +struct Instruction; +struct StaticFunctionAnalysis; +struct ProgramState; +struct ProgramStateDiff; // TODO: move this extern declaration of FxOS::insmap extern std::array, 65536> insmap; @@ -55,7 +57,7 @@ struct Function: public BinaryObject BasicBlock const &basicBlockByAddress(u32 pc) const; /* Get the entry block. */ - int entryBlockIndex() const + uint entryBlockIndex() const { return m_entryBlockIndex; } @@ -93,6 +95,11 @@ struct Function: public BinaryObject { return m_analysisVersion; } + /* Was this function analyzed? */ + bool hasAnalysis() const + { + return static_cast(m_analysisResult); + } /* Get analysis results if there are any. */ StaticFunctionAnalysis const *getAnalysis() const { @@ -249,6 +256,13 @@ struct BasicBlock return m_instructions.rend(); } + /* Entry state after analysis, if analysis was performed. */ + ProgramState const *initialState() const; + + /* TODO: Iterator over instructions that also give the program state at the + point of the instruction. If no analysis was performed, the pointer will + be null. */ + /* Functions for checking and setting flags */ u32 getFlags() const @@ -479,6 +493,9 @@ struct Instruction m_flags = flags; } + /* Effect on program state found by analysis, if analysis was performed. */ + ProgramStateDiff const *stateDiff() const; + /* Construction functions to be used only by the cfg pass */ void setBlockContext(uint blockIndex, uint insnIndex); diff --git a/include/fxos/view/assembly.h b/include/fxos/view/assembly.h index 17bf6e8..63fd465 100644 --- a/include/fxos/view/assembly.h +++ b/include/fxos/view/assembly.h @@ -85,8 +85,14 @@ struct ViewAssemblyOptions /* 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; + /* Whether to show essential analysis results such as call targets */ + bool essentialAnalysisResults = false; + + /* Whether to dump all the data from the function analysis */ + bool dumpFunctionAnalysis = false; + + /* Whether to print the complete program state at every instruction */ + bool showAllProgramStates = false; /* Whether to show details of instruction addresses and encodings */ bool showInstructionDetails = false; diff --git a/lib/analysis.cpp b/lib/analysis.cpp index 044034b..eca1db2 100644 --- a/lib/analysis.cpp +++ b/lib/analysis.cpp @@ -286,7 +286,7 @@ static ProgramStateDiff interpretInstruction( static void interpretBlock(BasicBlock const &bb, BlockStates &states) { - ProgramState PS(states.entry); + ProgramState PS {states.entry}; states.diffs.clear(); for(Instruction const &i: bb) { @@ -305,7 +305,7 @@ std::unique_ptr analyzeFunction(Function const &f) /* Initialize all blocks' entry states */ for(uint i = 0; i < f.blockCount(); i++) { BlockStates BS; - if(i == 0) + if(i == f.entryBlockIndex()) BS.entry.setFunctionInit(); else BS.entry.setBottom(); @@ -323,8 +323,8 @@ std::unique_ptr analyzeFunction(Function const &f) BasicBlock const &bb = f.basicBlockByIndex(i); VBS[i].nextEntry = VBS[i].entry; - for(int succIndex: bb.successorsByIndex()) - VBS[i].nextEntry.joinWith(VBS[succIndex].exit); + for(int predIndex: bb.predecessorsByIndex()) + VBS[i].nextEntry.joinWith(VBS[predIndex].exit); } /* Determine whether a fixpoint has been reached yet */ diff --git a/lib/function.cpp b/lib/function.cpp index 082da28..557c9f2 100644 --- a/lib/function.cpp +++ b/lib/function.cpp @@ -330,6 +330,15 @@ uint BasicBlock::blockIndex() const __builtin_unreachable(); } +ProgramState const *BasicBlock::initialState() const +{ + StaticFunctionAnalysis const *SFA = parentFunction().getAnalysis(); + if(!SFA) + return nullptr; + + return &SFA->blocks[blockIndex()].entry; +} + bool BasicBlock::mayFallthrough() const { Instruction const *ins = terminatorInstruction(); @@ -426,6 +435,15 @@ Instruction::Instruction(Function &function, u32 address, u32 opcode): m_flags = 0; } +ProgramStateDiff const *Instruction::stateDiff() const +{ + StaticFunctionAnalysis const *SFA = parentFunction().getAnalysis(); + if(!SFA) + return nullptr; + + return &SFA->blocks[m_blockIndex].diffs[m_insnIndex]; +} + void Instruction::setBlockContext(uint blockIndex, uint insnIndex) { m_blockIndex = blockIndex; diff --git a/lib/view/assembly.cpp b/lib/view/assembly.cpp index 2763321..ba7ece9 100644 --- a/lib/view/assembly.cpp +++ b/lib/view/assembly.cpp @@ -235,7 +235,25 @@ void viewAssemblyLegacyAddress( static ViewAssemblyOptions defaultOptions {}; -void viewAssemblyInstruction(Instruction const &ins, ViewAssemblyOptions *opts) +static void viewProgramState(ProgramState const &PS, std::string lineStart) +{ + ViewStringsOptions opts = { + .maxColumns = 70, + .lineStart = lineStart, + .separator = ", ", + .style = fmt::fg(fmt::terminal_color::cyan), + }; + RelConstDomain RCD; + std::cout << "\e[36m"; + viewStrings(std::views::iota(0, 16) | std::views::transform([&](int i) { + return fmt::format("r{}:{}", i, PS.getRegister(i).str(false)); + }), + opts); + std::cout << "\e[0m"; +} + +void viewAssemblyInstruction( + Instruction const &ins, ProgramState const *PS, ViewAssemblyOptions *opts) { opts = opts ? opts : &defaultOptions; @@ -243,6 +261,9 @@ void viewAssemblyInstruction(Instruction const &ins, ViewAssemblyOptions *opts) OperandOutput opout; u32 pc = ins.address(); + if(opts->showAllProgramStates && PS) + viewProgramState(*PS, fmt_rgb(" | ", fmt::color::gray)); + if(opts->showInstructionDetails) printf(" %08x: %04x ", pc, opcode.encoding()); else @@ -272,16 +293,23 @@ void viewAssemblyInstruction(Instruction const &ins, ViewAssemblyOptions *opts) std::vector> comments; - if(opts->printFunctionAnalysis) { - auto *an = ins.parentFunction().getAnalysis(); + auto *an = ins.parentFunction().getAnalysis(); + if(opts->dumpFunctionAnalysis && an) { + auto &block = an->blocks[ins.parentBlock().blockIndex()]; + ProgramStateDiff const &diff = block.diffs[ins.indexInBlock()]; + if(diff.target() != static_cast(ProgramStateDiff::Target::None)) + comments.emplace_back( + diff.str(), fmt::fg(fmt::terminal_color::cyan)); + } - if(an) { - auto &block = an->blocks[ins.parentBlock().blockIndex()]; - ProgramStateDiff const &diff = block.diffs[ins.indexInBlock()]; - if(diff.target() - != static_cast(ProgramStateDiff::Target::None)) - comments.emplace_back( - diff.str(), fmt::fg(fmt::terminal_color::cyan)); + switch(ins.opcode().operation()) { + case AsmInstruction::SH_jmp: + case AsmInstruction::SH_jsr: + if(PS) { + int n = ins.opcode().operand(0).base().getR(); + RelConst target = PS->getRegister(n); + comments.emplace_back( + target.str(false), fmt::fg(fmt::terminal_color::yellow)); } } @@ -337,23 +365,6 @@ static std::string objectsAt( }); } -static void viewProgramState(ProgramState const &PS, std::string lineStart) -{ - ViewStringsOptions opts = { - .maxColumns = 70, - .lineStart = lineStart, - .separator = ", ", - .style = fmt::fg(fmt::terminal_color::cyan), - }; - RelConstDomain RCD; - std::cout << "\e[36m"; - viewStrings(std::views::iota(0, 16) | std::views::transform([&](int i) { - return fmt::format("r{}:{}", i, PS.getRegister(i).str(false)); - }), - opts); - std::cout << "\e[0m"; -} - void viewAssemblyBasicBlock(BasicBlock const &bb, ViewAssemblyOptions *opts) { opts = opts ? opts : &defaultOptions; @@ -395,16 +406,26 @@ void viewAssemblyBasicBlock(BasicBlock const &bb, ViewAssemblyOptions *opts) printf("\n"); } - if(opts->printFunctionAnalysis) { - auto *an = bb.parentFunction().getAnalysis(); - if(an) { - auto &block = an->blocks[bb.blockIndex()]; - viewProgramState(block.entry, fmt_rgb(" | ", fmt::color::gray)); - } + auto *an = bb.parentFunction().getAnalysis(); + if(opts->dumpFunctionAnalysis && !opts->showAllProgramStates && an) { + auto &block = an->blocks[bb.blockIndex()]; + viewProgramState(block.entry, fmt_rgb(" | ", fmt::color::gray)); } - for(Instruction const &ins: bb) - viewAssemblyInstruction(ins, opts); + if(bb.parentFunction().hasAnalysis()) { + ProgramState PS = *bb.initialState(); + for(Instruction const &ins: bb) { + viewAssemblyInstruction(ins, &PS, opts); + PS.applyDiff(*ins.stateDiff()); + } + + if(opts->showAllProgramStates) + viewProgramState(PS, fmt_rgb(" | ", fmt::color::gray)); + } + else { + for(Instruction const &ins: bb) + viewAssemblyInstruction(ins, nullptr, opts); + } printf("\n"); } diff --git a/shell/d.cpp b/shell/d.cpp index f5bdb9d..308f732 100644 --- a/shell/d.cpp +++ b/shell/d.cpp @@ -22,6 +22,7 @@ struct _d_args { std::variant location; bool analyze = false; + int verbose = 0; }; static _d_args parse_d(Session &session, Parser &parser) @@ -32,6 +33,7 @@ static _d_args parse_d(Session &session, Parser &parser) return {}; parser.option("-a", [&args](Parser &) { args.analyze = true; }); + parser.option("-v", [&args](Parser &) { args.verbose++; }); parser.accept_options(); args.location = parser.expr_or_range(session.currentBinary()); @@ -39,14 +41,14 @@ static _d_args parse_d(Session &session, Parser &parser) return args; } -void _d(Session &session, std::variant location, bool analyze) +void _d(Session &session, struct _d_args const &args) { Binary *b = session.currentBinary(); if(!b) return FxOS_log(ERR, "No current binary!\n"); - if(std::holds_alternative(location)) { - Range range = std::get(location); + if(std::holds_alternative(args.location)) { + Range range = std::get(args.location); if(range.start & 1) { fmt::print("address 0x{:08x} is odd, starting at 0x{:08x}\n", range.start, range.start + 1); @@ -80,7 +82,7 @@ void _d(Session &session, std::variant location, bool analyze) viewAssemblyLegacyRegion(*b, r); } else { - uint32_t address = std::get(location); + uint32_t address = std::get(args.location); if(address & 1) { fmt::print("address 0x{:08x} is odd, starting at 0x{:08x}\n", @@ -90,13 +92,15 @@ void _d(Session &session, std::variant location, bool analyze) Function f(*b, address); if(f.exploreFunctionAt(address)) { - if(analyze) + if(args.analyze) f.runAnalysis(); ViewAssemblyOptions opts; opts.binary = b; - opts.printFunctionAnalysis = analyze; - opts.showInstructionDetails = !analyze; + opts.essentialAnalysisResults = args.analyze; + opts.dumpFunctionAnalysis = args.analyze && (args.verbose >= 1); + opts.showAllProgramStates = args.analyze && (args.verbose >= 2); + opts.showInstructionDetails = !args.analyze; viewAssemblyFunction(f, &opts); } } @@ -110,10 +114,10 @@ static ShellCommand _d_cmd( "d", [](Session &s, Parser &p) { auto args = parse_d(s, p); - _d(s, args.location, args.analyze); + _d(s, args); }, [](Session &s, Parser &p) { parse_d(s, p); }, "Disassemble", R"( -d [-a]
+d [-a] [-v]*
d Disassembles code. In the first form, explores a function at the provided @@ -121,6 +125,11 @@ address (without modifying the binary), exploring branches until function terminators, invalid instructions or dynamically-computed jumps. If -a is specified, also perform static analysis. +The flag -v enables verbose output and can be specified multiple times. + 0: Essential information dervied from analysis + 1: Dump complete analysis results + 2: Show full derived program state at every program point + In the second form, disassembles instructions in the specified range. (This is an older feature with less support for advanced features.) )");