From 2dbd9103799ba9bc44f3a531ede8f58c487b6472 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sun, 20 Aug 2023 20:22:12 +0200 Subject: [PATCH] fxos, _if: add insufficient call analysis We look for constants in call instruction parameters, but this only works for jsr because the register argument in [jmp @rn] is not known to be a constant yet (some static analysis required). --- include/fxos/disassembly.h | 6 ++++ include/fxos/lang.h | 2 ++ lib/disassembly.cpp | 17 +++++++++++ lib/lang.cpp | 6 ++++ lib/passes/cfg.cpp | 32 +++++++++++++++++--- shell/i.cpp | 62 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+), 5 deletions(-) diff --git a/include/fxos/disassembly.h b/include/fxos/disassembly.h index 617e336..d0e2449 100644 --- a/include/fxos/disassembly.h +++ b/include/fxos/disassembly.h @@ -108,6 +108,10 @@ struct Instruction struct Function { + /* Create a bare function with no detailed information */ + Function(uint32_t pc); + + /* Function's entry point */ uint32_t address; /* List of subfunctions called. TODO: Not yet populated by anyone */ @@ -188,6 +192,8 @@ struct Disassembly bool hasFunctionAt(uint32_t pc); /* Find a function by address; returns nullptr if not yet defined */ Function *getFunctionAt(uint32_t pc); + /* Find a function and create it empty if it's not yet defined */ + Function *getOrCreateFunctionAt(uint32_t pc); // Claim information diff --git a/include/fxos/lang.h b/include/fxos/lang.h index d4a48f1..90c86ac 100644 --- a/include/fxos/lang.h +++ b/include/fxos/lang.h @@ -175,6 +175,8 @@ struct AsmInstruction bool isjump() const noexcept; /* Check whether it's a conditional jump */ bool iscondjump() const noexcept; + /* Check whether instruction is a function call */ + bool iscall() const noexcept; /* Check whether instruction has a delay slot */ bool isdelayed() const noexcept; /* Check whether instruction can be used in a delay slot */ diff --git a/lib/disassembly.cpp b/lib/disassembly.cpp index 6a987bd..9142315 100644 --- a/lib/disassembly.cpp +++ b/lib/disassembly.cpp @@ -51,6 +51,14 @@ Instruction::Instruction(uint16_t opcode): { } +//--- +// Function information +//--- + +Function::Function(uint32_t pc): address {pc} +{ +} + //--- // Dynamic claims //--- @@ -146,6 +154,15 @@ Function *Disassembly::getFunctionAt(uint32_t pc) return &it->second; } +Function *Disassembly::getOrCreateFunctionAt(uint32_t pc) +{ + if(!this->hasFunctionAt(pc)) { + Function f(pc); + this->functions.insert({pc, f}); + } + return this->getFunctionAt(pc); +} + std::vector Disassembly::findClaimConflicts( uint32_t address, int size, int max) { diff --git a/lib/lang.cpp b/lib/lang.cpp index 40f3efd..f3d386b 100644 --- a/lib/lang.cpp +++ b/lib/lang.cpp @@ -261,6 +261,12 @@ bool AsmInstruction::iscondjump() const noexcept return false; } +bool AsmInstruction::iscall() const noexcept +{ + return !strcmp(mnemonic, "jsr") || !strcmp(mnemonic, "bsr") + || !strcmp(mnemonic, "bsrf"); +} + bool AsmInstruction::isdelayed() const noexcept { char const *v[] = { diff --git a/lib/passes/cfg.cpp b/lib/passes/cfg.cpp index eeb7942..22b1d08 100644 --- a/lib/passes/cfg.cpp +++ b/lib/passes/cfg.cpp @@ -103,13 +103,35 @@ bool CfgPass::exploreFunction(uint32_t pc) m_lastFunction = pc; m_claimedInstructions.clear(); - if(!m_disasm.hasFunctionAt(pc)) { - // TODO: Have proper function creation methods in Disassembly - Function func = {.address = pc, .callTargets = {}}; - m_disasm.functions[pc] = func; + Function *func = m_disasm.getOrCreateFunctionAt(pc); + if(!this->analyzeFunction(pc)) + return false; + + RelConstDomain RCD; + + /* Look for call targets */ + for(uint32_t pc: m_claimedInstructions) { + Instruction const *ci = m_disasm.getInstructionAt(pc); + if(!ci) + continue; + AsmInstruction const &i = *ci->inst; + + /* Find function call instructions */ + if(i.isterminal() || !i.iscall() || i.arg_count < 1) + continue; + + /* The target must be known */ + if(!RCD.is_constant(ci->args[0].location)) + continue; + + uint32_t target = RCD.constant_value(ci->args[0].location); + auto &v = func->callTargets; + + if(std::find(v.begin(), v.end(), target) == v.end()) + func->callTargets.push_back(target); } - return this->analyzeFunction(pc); + return true; } std::set CfgPass::resultClaims() diff --git a/shell/i.cpp b/shell/i.cpp index 2bda554..43c53b7 100644 --- a/shell/i.cpp +++ b/shell/i.cpp @@ -42,6 +42,56 @@ void _ic(Session &session, struct _ic_args const &args) } } +//--- +// if +//--- + +struct _if_args +{ + std::vector addresses; +}; + +static struct _if_args parse_if(Session &session, Parser &parser) +{ + _if_args args; + + while(!parser.at_end()) + args.addresses.push_back(parser.expr(session.current_space)); + + parser.end(); + return args; +} + +void _if(Session &session, struct _if_args const &args) +{ + if(!session.current_space) + return; + Disassembly &disasm = session.current_space->disasm; + + if(!args.addresses.size()) { + fmt::print("{} functions\n", disasm.functions.size()); + } + + for(uint32_t address: args.addresses) { + Function *func = disasm.getFunctionAt(address); + if(!func) { + FxOS_log(ERR, "no function at 0x{:08x}", address); + continue; + } + + // TODO: Promote address to syscall, name, etc. + fmt::print("0x{:08x}:\n", address); + fmt::print(" callTargets:\n"); + + auto &ct = func->callTargets; + // TODO: Promote address to syscall, name, etc. + for(uint32_t pc: ct) + fmt::print(" 0x{:08x}\n", pc); + if(!ct.size()) + fmt::print(" (none)\n"); + } +} + //--- // io //--- @@ -384,6 +434,18 @@ usually generated by analysis commands and allow sections of the OS to be marked as part of functions, data, interrupt handlers, etc. )"); +static ShellCommand _if_cmd( + "if", [](Session &s, Parser &p) { + auto args = parse_if(s, p); + _if(s, args); + }, + [](Session &s, Parser &p) { parse_if(s, p); }, "Info Function", R"( +if [...] + +Prints information about functions. Without arguments, prints vspace-level +statistics. With arguments, prints detailed function info. +)"); + static ShellCommand _io_cmd( "io", [](Session &s, Parser &p) { _io(s, parse_io(s, p)); }, [](Session &s, Parser &p) { parse_io(s, p); }, "Info OS", R"(