fxos: print call targets in [d -a]
This commit is contained in:
parent
ed902db2d8
commit
944745d0e3
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
27
shell/d.cpp
27
shell/d.cpp
|
@ -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.)
|
||||
)");
|
||||
|
|
Loading…
Reference in New Issue