fxos: print call targets in [d -a]

This commit is contained in:
Lephenixnoir 2023-11-29 17:33:22 +01:00
parent ed902db2d8
commit 944745d0e3
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
6 changed files with 126 additions and 55 deletions

View File

@ -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<std::optional<AsmInstruction>, 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<bool>(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);

View File

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

View File

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

View File

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

View File

@ -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<std::pair<std::string, fmt::text_style>> 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<int>(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<int>(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");
}

View File

@ -22,6 +22,7 @@ struct _d_args
{
std::variant<long, Range> 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<long, Range> 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<Range>(location)) {
Range range = std::get<Range>(location);
if(std::holds_alternative<Range>(args.location)) {
Range range = std::get<Range>(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<long, Range> location, bool analyze)
viewAssemblyLegacyRegion(*b, r);
}
else {
uint32_t address = std::get<long>(location);
uint32_t address = std::get<long>(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<long, Range> 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] <address>
d [-a] [-v]* <address>
d <range>
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.)
)");