Compare commits
20 Commits
expr-and-r
...
master
Author | SHA1 | Date |
---|---|---|
Dr-Carlos | ac7f1eb073 | |
Dr-Carlos | 46aad4e2d6 | |
Lephenixnoir | 0373ae50fe | |
Lephenixnoir | 2dbd910379 | |
Lephenixnoir | df4bba2c1a | |
Lephenixnoir | 12e6cd45a4 | |
Lephenixnoir | 0f23fec85d | |
Lephenixnoir | 44babe3baf | |
Lephenixnoir | 97029d4f3e | |
Lephenixnoir | a399ed31d7 | |
Dr-Carlos | 5e20cbe805 | |
Dr-Carlos | efaad5b980 | |
Dr-Carlos | b494a30404 | |
Dr-Carlos | f16ecc370c | |
Dr-Carlos | 5a3de5aa17 | |
Lephenixnoir | bbd25b625c | |
Dr-Carlos | 111620a649 | |
Dr-Carlos | c66ae1d5c6 | |
Dr-Carlos | 2f2e4bb1d5 | |
Dr-Carlos | 1082c451d2 |
|
@ -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 */
|
||||
|
@ -144,6 +148,9 @@ struct Claim
|
|||
/* Utility to check for intersections */
|
||||
bool intersects(Claim const &other) const;
|
||||
|
||||
/* Check equality of claims (raw equality) */
|
||||
bool operator==(Claim const &other) const;
|
||||
|
||||
/* String representation */
|
||||
std::string str() const;
|
||||
};
|
||||
|
@ -185,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
|
||||
|
@ -194,13 +203,20 @@ struct Disassembly
|
|||
/* Access the claim that owns the address, if there is one */
|
||||
Claim const *getClaimAt(uint32_t address);
|
||||
|
||||
/* Access the first claim that overlaps this region, if any */
|
||||
/* Find the first claim that overlaps this region, if any */
|
||||
Claim const *findClaimConflict(uint32_t address, int size);
|
||||
|
||||
/* Find all (or up to max ≥ 0) claims that overlaps this region */
|
||||
std::vector<Claim const *> findClaimConflicts(
|
||||
uint32_t address, int size, int max = -1);
|
||||
|
||||
/* Add a new exclusive claim. If there is any intersection with previous
|
||||
claims, this fails. */
|
||||
claims which do not compare equal to c, this fails. */
|
||||
bool addExclusiveClaim(Claim const &c);
|
||||
|
||||
/* Get all claims owned by a certain address. */
|
||||
std::vector<Claim const *> findClaimsOwnedBy(uint32_t address);
|
||||
|
||||
// TODO: Add non-exclusive claims/handle collisions
|
||||
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -32,7 +32,6 @@ namespace FxOS {
|
|||
|
||||
struct MemoryArea
|
||||
{
|
||||
public:
|
||||
/* Userspace seen from user and privileged mode */
|
||||
static MemoryArea U0, P0;
|
||||
/* Second half of memory, only for privileged mode */
|
||||
|
@ -98,7 +97,7 @@ struct MemoryRegion
|
|||
/* Returns the size of the region */
|
||||
uint32_t size() const noexcept;
|
||||
|
||||
/* Returns the area associated to the region (assuming it is fully
|
||||
/* Returns the area associated with the region (assuming it is fully
|
||||
contained in one, which should always be the case) */
|
||||
MemoryArea area() const noexcept;
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@ class OS
|
|||
{
|
||||
public:
|
||||
/* Create an OS interface for this virtual space. If there is no data
|
||||
loaded in ROM or the OS can't be identified, the type os OS is set to
|
||||
UNKNOWN and no information is provided. */
|
||||
loaded in ROM or the OS can't be identified, the type os OS is set to
|
||||
UNKNOWN and no information is provided. */
|
||||
OS(VirtualSpace &space);
|
||||
|
||||
/* Type of OS, determined at construction */
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#define FXOS_PASSES_CFG_H
|
||||
|
||||
#include <fxos/disassembly.h>
|
||||
#include <fxos/passes/pcrel.h>
|
||||
#include <set>
|
||||
|
||||
namespace FxOS {
|
||||
|
@ -69,6 +70,8 @@ private:
|
|||
uint32_t m_lastFunction;
|
||||
/* Set of instructions in a function, used to generate new claims */
|
||||
std::set<uint32_t> m_claimedInstructions;
|
||||
/* pcrel pass used to find call to other functions */
|
||||
PcrelPass m_pcrel;
|
||||
};
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
|
|
@ -87,7 +87,7 @@ struct BitfieldType: public BaseType
|
|||
should equal the size of the array type. */
|
||||
struct ArrayType: public BaseType
|
||||
{
|
||||
struct DataType *object_type;
|
||||
class DataType *object_type;
|
||||
int elements;
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace FxOS {
|
||||
|
||||
|
@ -34,6 +35,40 @@ struct Symbol
|
|||
|
||||
/* Symbol name, no particular conventions */
|
||||
std::string name;
|
||||
|
||||
bool operator<(const FxOS::Symbol &right) const
|
||||
{
|
||||
return (type < right.type)
|
||||
|| (value < right.value && type == right.type);
|
||||
}
|
||||
|
||||
bool operator>(const FxOS::Symbol &right) const
|
||||
{
|
||||
return (type > right.type)
|
||||
|| (value > right.value && type == right.type);
|
||||
}
|
||||
|
||||
bool operator==(const FxOS::Symbol &right) const
|
||||
{
|
||||
return value == right.value && type == right.type;
|
||||
}
|
||||
|
||||
bool operator!=(const FxOS::Symbol &right) const
|
||||
{
|
||||
return value != right.value || type != right.type;
|
||||
}
|
||||
|
||||
bool operator>=(const FxOS::Symbol &right) const
|
||||
{
|
||||
return (type > right.type)
|
||||
|| (value >= right.value && type == right.type);
|
||||
}
|
||||
|
||||
bool operator<=(const FxOS::Symbol &right) const
|
||||
{
|
||||
return (type < right.type)
|
||||
|| (value <= right.value && type == right.type);
|
||||
}
|
||||
};
|
||||
|
||||
/* A symbol table, usually the set of symbols of a virtual space */
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
|
||||
// |_ base# + offset |_| /_\_\___/__/ //
|
||||
//---------------------------------------------------------------------------//
|
||||
// fxos/util/Queue: Simple queue that handles recursivity
|
||||
// fxos/util/Queue: Simple queue that handles recursion
|
||||
//
|
||||
// Can be instantiated for any T for which std::set<T> is valid and properly
|
||||
// checks objects' identity.
|
||||
//---
|
||||
|
||||
#ifndef FXOS_UTIL_QUEUE_H
|
||||
|
|
|
@ -28,14 +28,14 @@ auto constexpr Bottom = RelConst::Bottom;
|
|||
return bottom(); \
|
||||
}
|
||||
|
||||
inline RelConst RelConstDomain::bottom() const noexcept
|
||||
RelConst RelConstDomain::bottom() const noexcept
|
||||
{
|
||||
RelConst b {};
|
||||
b.spe = Bottom;
|
||||
return b;
|
||||
}
|
||||
|
||||
inline RelConst RelConstDomain::top() const noexcept
|
||||
RelConst RelConstDomain::top() const noexcept
|
||||
{
|
||||
RelConst b {};
|
||||
b.spe = Top;
|
||||
|
@ -300,12 +300,17 @@ std::string RelConst::str() const noexcept
|
|||
|
||||
if(ival >= -256 && ival < 256) {
|
||||
uint32_t v = 0;
|
||||
if(str.size() && ival > 0)
|
||||
str += "+", v = ival;
|
||||
if(str.size() && ival < 0)
|
||||
str += "-", v = -ival;
|
||||
if(ival >= 0) {
|
||||
if(str.size())
|
||||
str += "+";
|
||||
v = ival;
|
||||
}
|
||||
else {
|
||||
str += "-";
|
||||
v = -ival;
|
||||
}
|
||||
|
||||
return str + format("%d", v);
|
||||
return str + format("%d (0x%08x)", v, uval);
|
||||
}
|
||||
else {
|
||||
return str + format("0x%08x", uval);
|
||||
|
|
|
@ -51,6 +51,14 @@ Instruction::Instruction(uint16_t opcode):
|
|||
{
|
||||
}
|
||||
|
||||
//---
|
||||
// Function information
|
||||
//---
|
||||
|
||||
Function::Function(uint32_t pc): address {pc}
|
||||
{
|
||||
}
|
||||
|
||||
//---
|
||||
// Dynamic claims
|
||||
//---
|
||||
|
@ -65,6 +73,12 @@ bool Claim::intersects(Claim const &other) const
|
|||
return inter_start < inter_end;
|
||||
}
|
||||
|
||||
bool Claim::operator==(Claim const &other) const
|
||||
{
|
||||
return this->address == other.address && this->size == other.size
|
||||
&& this->type == other.type && this->owner == other.owner;
|
||||
}
|
||||
|
||||
std::string Claim::str() const
|
||||
{
|
||||
std::string details = format(" (claim 0x%08x:%d)", address, size);
|
||||
|
@ -140,7 +154,17 @@ Function *Disassembly::getFunctionAt(uint32_t pc)
|
|||
return &it->second;
|
||||
}
|
||||
|
||||
Claim const *Disassembly::findClaimConflict(uint32_t address, int size)
|
||||
Function *Disassembly::getOrCreateFunctionAt(uint32_t pc)
|
||||
{
|
||||
if(!this->hasFunctionAt(pc)) {
|
||||
Function f(pc);
|
||||
this->functions.insert({pc, f});
|
||||
}
|
||||
return this->getFunctionAt(pc);
|
||||
}
|
||||
|
||||
std::vector<Claim const *> Disassembly::findClaimConflicts(
|
||||
uint32_t address, int size, int max)
|
||||
{
|
||||
Claim fake_claim = {
|
||||
.address = address,
|
||||
|
@ -148,6 +172,7 @@ Claim const *Disassembly::findClaimConflict(uint32_t address, int size)
|
|||
.type = 0,
|
||||
.owner = 0,
|
||||
};
|
||||
std::vector<Claim const *> conflicts;
|
||||
|
||||
/* Find the first claim whose start is [> address] */
|
||||
auto it = this->claims.upper_bound(fake_claim);
|
||||
|
@ -155,19 +180,26 @@ Claim const *Disassembly::findClaimConflict(uint32_t address, int size)
|
|||
if(it != this->claims.begin())
|
||||
it--;
|
||||
|
||||
while(it != this->claims.end()) {
|
||||
while(it != this->claims.end()
|
||||
&& (max < 0 || conflicts.size() < (size_t)max)) {
|
||||
/* We completely passed address+size, no conflict found */
|
||||
if(it->address >= address + size)
|
||||
return nullptr;
|
||||
break;
|
||||
|
||||
/* There is an intersection */
|
||||
if(it->intersects(fake_claim))
|
||||
return &*it;
|
||||
conflicts.push_back(&*it);
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
Claim const *Disassembly::findClaimConflict(uint32_t address, int size)
|
||||
{
|
||||
auto claims = findClaimConflicts(address, size, 1);
|
||||
return claims.size() > 0 ? claims[0] : nullptr;
|
||||
}
|
||||
|
||||
Claim const *Disassembly::getClaimAt(uint32_t address)
|
||||
|
@ -177,15 +209,35 @@ Claim const *Disassembly::getClaimAt(uint32_t address)
|
|||
|
||||
bool Disassembly::addExclusiveClaim(Claim const &c)
|
||||
{
|
||||
Claim const *conflict = this->findClaimConflict(c.address, c.size);
|
||||
if(conflict) {
|
||||
auto conflicts = this->findClaimConflicts(c.address, c.size);
|
||||
bool exclusive = true;
|
||||
|
||||
for(auto conflict: conflicts) {
|
||||
/* Allow declaring the same claim twice */
|
||||
if(*conflict == c)
|
||||
continue;
|
||||
|
||||
FxOS_log(ERR, "exclusive claim for %s conflicts with %s", c.str(),
|
||||
conflict->str());
|
||||
return false;
|
||||
conflicts[0]->str());
|
||||
exclusive = false;
|
||||
}
|
||||
|
||||
this->claims.insert(c);
|
||||
return true;
|
||||
if(exclusive)
|
||||
this->claims.insert(c);
|
||||
return exclusive;
|
||||
}
|
||||
|
||||
std::vector<Claim const *> Disassembly::findClaimsOwnedBy(uint32_t address)
|
||||
{
|
||||
std::vector<Claim const *> claims;
|
||||
|
||||
/* Since we don't order by owner we have to tank the linear search */
|
||||
for(auto const &c: this->claims) {
|
||||
if(c.owner == address)
|
||||
claims.push_back(&c);
|
||||
}
|
||||
|
||||
return claims;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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[] = {
|
||||
|
@ -288,6 +294,7 @@ bool AsmInstruction::isdelayed() const noexcept
|
|||
|
||||
bool AsmInstruction::isvaliddelayslot() const noexcept
|
||||
{
|
||||
// TODO: PC-relative move is a valid delay slot but it doesn't work
|
||||
return !isdelayed() && !isterminal() && !isjump() && !iscondjump()
|
||||
&& strcmp(this->mnemonic, "mova") != 0;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
namespace FxOS {
|
||||
|
||||
CfgPass::CfgPass(Disassembly &disasm):
|
||||
InstructionPass(disasm), m_claimedInstructions {}
|
||||
InstructionPass(disasm), m_claimedInstructions {}, m_pcrel {disasm}
|
||||
{
|
||||
this->setAllowDiscovery(true);
|
||||
}
|
||||
|
@ -94,6 +94,7 @@ bool CfgPass::analyzeInstruction(uint32_t pc, Instruction &i)
|
|||
i.jmptarget = jmptarget;
|
||||
}
|
||||
|
||||
m_pcrel.analyzeInstruction(pc, i);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -102,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<Claim> CfgPass::resultClaims()
|
||||
|
|
124
shell/a.cpp
124
shell/a.cpp
|
@ -50,20 +50,6 @@ static void ad_disassemble_all(
|
|||
printf("\n");
|
||||
FxOS_log(LOG, "Finished pass <cfg> in %s", timer.format_time());
|
||||
|
||||
/* Annotate all decoded instructions with pcrel/syscall
|
||||
TODO: analyze only the functions, if possible */
|
||||
printr("[pcrel] Resolving PC-relative addressing modes...");
|
||||
timer.restart();
|
||||
PcrelPass pcrel_pass(space.disasm);
|
||||
if(!pcrel_pass.analyzeAllInstructions()) {
|
||||
errors++;
|
||||
if(!force)
|
||||
return;
|
||||
}
|
||||
timer.stop();
|
||||
printf("\n");
|
||||
FxOS_log(LOG, "Finished pass <pcrel> in %s", timer.format_time());
|
||||
|
||||
printr("[syscall] Finding syscall references...");
|
||||
timer.restart();
|
||||
OS *os = space.os_analysis();
|
||||
|
@ -142,6 +128,103 @@ void _ads(Session &session)
|
|||
ad_disassemble_all(space, addresses, true);
|
||||
}
|
||||
|
||||
//---
|
||||
// am
|
||||
//---
|
||||
|
||||
static void _am_cg_main_menu_function(VirtualSpace &vspace)
|
||||
{
|
||||
OS *os = vspace.os_analysis();
|
||||
if(!os) {
|
||||
FxOS_log(ERR, "no OS analysis");
|
||||
return;
|
||||
}
|
||||
|
||||
if(os->syscall_count() < 0x1e58) {
|
||||
FxOS_log(ERR, "less than 0x1e58 syscalls");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t sc_addr = os->syscall(0x1e58);
|
||||
fmt::print("syscall %%1e58 found at 0x{:08x}\n", sc_addr);
|
||||
|
||||
/* Check up to 150 instructions to find the call to the internal function
|
||||
SaveAndOpenMainMenu(). This call is in a widget of the shape
|
||||
|
||||
mov.l GetkeyToMainFunctionReturnFlag, rX
|
||||
mov #3, rY
|
||||
bsr SaveAndOpenMainMenu
|
||||
mov.b rY, @rX
|
||||
bra <start of widget>
|
||||
nop */
|
||||
for(int i = 0; i < 150; i++) {
|
||||
uint16_t i0 = vspace.read_u16(sc_addr + 2 * (i + 0));
|
||||
uint16_t i1 = vspace.read_u16(sc_addr + 2 * (i + 1));
|
||||
uint16_t i2 = vspace.read_u16(sc_addr + 2 * (i + 2));
|
||||
uint16_t i3 = vspace.read_u16(sc_addr + 2 * (i + 3));
|
||||
uint16_t i4 = vspace.read_u16(sc_addr + 2 * (i + 4));
|
||||
uint16_t i5 = vspace.read_u16(sc_addr + 2 * (i + 5));
|
||||
|
||||
/* Match: mov.l @(disp, pc), rX */
|
||||
if((i0 & 0xf000) != 0xd000)
|
||||
continue;
|
||||
int rX = (i0 >> 8) & 0x0f;
|
||||
|
||||
/* Match: mov #3, rY */
|
||||
if((i1 & 0xf0ff) != 0xe003)
|
||||
continue;
|
||||
int rY = (i1 >> 8) & 0x0f;
|
||||
|
||||
/* Match: bsr @(disp, pc) */
|
||||
if((i2 & 0xf000) != 0xb000)
|
||||
continue;
|
||||
int disp = (i2 & 0x0fff);
|
||||
|
||||
/* Match: mov.b rX, @rY */
|
||||
if((i3 != 0x2000 + (rX << 8) + (rY << 4)))
|
||||
continue;
|
||||
|
||||
/* Match: bra @(_, pc) */
|
||||
if((i4 & 0xf000) != 0xa000)
|
||||
continue;
|
||||
|
||||
/* Match: nop */
|
||||
if(i5 != 0x0009)
|
||||
continue;
|
||||
|
||||
/* Return the target of the bsr instruction */
|
||||
uint32_t fun_addr = sc_addr + 2 * (i + 2) + 4 + disp * 2;
|
||||
|
||||
fmt::print("found widget at 0x{:08x}\n", sc_addr + 2 * i);
|
||||
fmt::print("rX = r{}, rY = r{}, disp = {}\n", rX, rY, disp);
|
||||
fmt::print("main menu function address: 0x{:08x}\n", fun_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string parse_am(Session &session, Parser &parser)
|
||||
{
|
||||
if(!session.current_space)
|
||||
return "";
|
||||
|
||||
std::string name = parser.symbol();
|
||||
parser.end();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
void _am(Session &session, std::string name)
|
||||
{
|
||||
if(!session.current_space) {
|
||||
FxOS_log(ERR, "am: no virtual space");
|
||||
return;
|
||||
}
|
||||
|
||||
if(name == "cg_main_menu_function")
|
||||
_am_cg_main_menu_function(*session.current_space);
|
||||
else
|
||||
FxOS_log(ERR, "am: unknown misc. command '%s'", name);
|
||||
}
|
||||
|
||||
//---
|
||||
// Command definitions
|
||||
//---
|
||||
|
@ -174,3 +257,16 @@ Disassembles all syscalls entries using ad, which stores the results in the
|
|||
current virtual space's main disassembly. Unlike ad, this commands continues
|
||||
even if some syscalls fail to disassemble.
|
||||
)");
|
||||
|
||||
static ShellCommand _am_cmd(
|
||||
"am",
|
||||
[](Session &s, Parser &p) {
|
||||
auto name = parse_am(s, p);
|
||||
_am(s, name);
|
||||
},
|
||||
[](Session &s, Parser &p) { parse_am(s, p); }, "Analysis: Misc functions",
|
||||
R"(
|
||||
am <name>
|
||||
|
||||
Runs miscellaneous analysis functions; commonly used for prototyping.
|
||||
)");
|
||||
|
|
119
shell/d.cpp
119
shell/d.cpp
|
@ -69,72 +69,69 @@ static void disassemble(Session &session, Disassembly &disasm,
|
|||
// d
|
||||
//---
|
||||
|
||||
static uint32_t parse_d(Session &session, Parser &parser)
|
||||
struct _d_args
|
||||
{
|
||||
if(!session.current_space)
|
||||
return 0;
|
||||
uint32_t address = session.current_space->cursor;
|
||||
std::variant<long, Range> location;
|
||||
};
|
||||
|
||||
if(!parser.at_end())
|
||||
address = parser.expr(session.current_space);
|
||||
static _d_args parse_d(Session &session, Parser &parser)
|
||||
{
|
||||
_d_args args;
|
||||
|
||||
if(!session.current_space)
|
||||
return {};
|
||||
|
||||
args.location = parser.at_end()
|
||||
? session.current_space->cursor
|
||||
: parser.expr_or_range(session.current_space);
|
||||
|
||||
parser.end();
|
||||
return address;
|
||||
return args;
|
||||
}
|
||||
|
||||
void _d(Session &session, uint32_t address)
|
||||
void _d(Session &session, std::variant<long, Range> location)
|
||||
{
|
||||
if(!session.current_space)
|
||||
return;
|
||||
FxOS::Disassembly disasm(*session.current_space);
|
||||
|
||||
if(address & 1) {
|
||||
fmt::print("address 0x{:08x} is odd, starting at 0x{:08x}\n", address,
|
||||
address + 1);
|
||||
address++;
|
||||
|
||||
if(std::holds_alternative<Range>(location)) {
|
||||
Range range = std::get<Range>(location);
|
||||
if(range.start & 1) {
|
||||
fmt::print("address 0x{:08x} is odd, starting at 0x{:08x}\n",
|
||||
range.start, range.start + 1);
|
||||
range.start++;
|
||||
}
|
||||
if(range.end & 1) {
|
||||
fmt::print("address 0x{:08x} is odd, ending at 0x{:08x}\n",
|
||||
range.end, range.end - 1);
|
||||
range.end--;
|
||||
}
|
||||
|
||||
if(range.start >= range.end)
|
||||
return;
|
||||
|
||||
/* Load the block into memory */
|
||||
for(uint32_t pc = range.start; pc < range.end; pc += 2)
|
||||
disasm.getInstructionAt(pc, true);
|
||||
|
||||
disassemble(session, disasm,
|
||||
{"pcrel", /*"constprop",*/ "syscall", "print"}, -1);
|
||||
}
|
||||
else {
|
||||
uint32_t address = std::get<long>(location);
|
||||
|
||||
disassemble(session, disasm,
|
||||
{"cfg", "pcrel", /*"constprop",*/ "syscall", "print"}, address);
|
||||
}
|
||||
if(address & 1) {
|
||||
fmt::print("address 0x{:08x} is odd, starting at 0x{:08x}\n",
|
||||
address, address + 1);
|
||||
address++;
|
||||
}
|
||||
|
||||
//---
|
||||
// dr
|
||||
//---
|
||||
|
||||
static Range parse_dr(Session &session, Parser &parser)
|
||||
{
|
||||
Range range = parser.range(session.current_space);
|
||||
parser.end();
|
||||
return range;
|
||||
}
|
||||
|
||||
void _dr(Session &session, Range range)
|
||||
{
|
||||
if(!session.current_space)
|
||||
return;
|
||||
FxOS::Disassembly disasm(*session.current_space);
|
||||
|
||||
if(range.start & 1) {
|
||||
fmt::print("address 0x{:08x} is odd, starting at 0x{:08x}\n",
|
||||
range.start, range.start + 1);
|
||||
range.start++;
|
||||
/* cfg implicitly does pcrel */
|
||||
disassemble(session, disasm,
|
||||
{"cfg", /*"constprop",*/ "syscall", "print"}, address);
|
||||
}
|
||||
if(range.end & 1) {
|
||||
fmt::print("address 0x{:08x} is odd, ending at 0x{:08x}\n", range.end,
|
||||
range.end - 1);
|
||||
range.end--;
|
||||
}
|
||||
|
||||
if(range.start >= range.end)
|
||||
return;
|
||||
|
||||
/* Load the block into memory */
|
||||
for(uint32_t pc = range.start; pc < range.end; pc += 2)
|
||||
disasm.getInstructionAt(pc, true);
|
||||
|
||||
disassemble(
|
||||
session, disasm, {"pcrel", /*"constprop",*/ "syscall", "print"}, -1);
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -142,9 +139,13 @@ void _dr(Session &session, Range range)
|
|||
//---
|
||||
|
||||
static ShellCommand _d_cmd(
|
||||
"d", [](Session &s, Parser &p) { _d(s, parse_d(s, p)); },
|
||||
"d",
|
||||
[](Session &s, Parser &p) {
|
||||
auto args = parse_d(s, p);
|
||||
_d(s, args.location);
|
||||
},
|
||||
[](Session &s, Parser &p) { parse_d(s, p); }, "Disassemble", R"(
|
||||
d [<address>]
|
||||
d [<address|range>]
|
||||
|
||||
Disassembles code starting at the specified address, exploring branches until
|
||||
function terminators, invalid instructions, or dynamically-computed jumps. The
|
||||
|
@ -159,15 +160,3 @@ The following disassembler passes are run:
|
|||
pcrel Computes PC-relative addresses (eg mov.l, mova, bf, bra...)
|
||||
syscall Annotates uses of syscall table entries with the syscall number
|
||||
)");
|
||||
|
||||
static ShellCommand _dr_cmd(
|
||||
"dr", [](Session &s, Parser &p) { _dr(s, parse_dr(s, p)); },
|
||||
[](Session &s, Parser &p) { parse_dr(s, p); }, "Disassemble Range", R"(
|
||||
dr [<range>]
|
||||
|
||||
Disassembles an explicit region of memory. This is similar to d, except that
|
||||
the disassembled code is pre-loaded from the region instead of being explored
|
||||
by the cfg pass. See d? for more information.
|
||||
|
||||
Like d, this command does not extend the virtual space's main disassembly.
|
||||
)");
|
||||
|
|
13
shell/e.cpp
13
shell/e.cpp
|
@ -30,8 +30,8 @@ static _e_args parse_e(Session &session, Parser &parser)
|
|||
if(!args.space_name.empty()) {
|
||||
space = session.get_space(args.space_name);
|
||||
if(!space) {
|
||||
std::string msg =
|
||||
format("virtual space '%s' does not exist", args.space_name);
|
||||
std::string msg
|
||||
= format("virtual space '%s' does not exist", args.space_name);
|
||||
if(parser.completing())
|
||||
throw Parser::CompletionRequest("_error", msg);
|
||||
else
|
||||
|
@ -49,14 +49,15 @@ static _e_args parse_e(Session &session, Parser &parser)
|
|||
void _e(Session &, std::string, std::vector<long> const &values)
|
||||
{
|
||||
for(long value: values) {
|
||||
long print_val = labs(value);
|
||||
|
||||
/* Hexa format */
|
||||
int length = (labs(value) <= (1ll << 32) ? 8 : 16) + 2 + (value < 0);
|
||||
int length = (print_val <= (1ll << 32) ? 8 : 16) + 2 + (value < 0);
|
||||
std::string format = fmt::format("{{:#0{}x}}", length);
|
||||
fmt::print(format, value);
|
||||
|
||||
long a = abs(value);
|
||||
if(a <= 100 || a % 100 <= 1 || a % 100 >= 99)
|
||||
fmt::print(" = {}", a);
|
||||
if(print_val <= 100 || print_val % 100 <= 1 || print_val % 100 >= 99)
|
||||
fmt::print(" = {}", print_val);
|
||||
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
|
|
@ -38,10 +38,10 @@ void _h_hexdump(Session &session, Range r, Selections sel)
|
|||
fmt::print(" ");
|
||||
}
|
||||
else if(is_selected(addr, sel)) {
|
||||
fmt::print(theme(13), "{:02x}", v.read_u8(addr));
|
||||
fmt::print(theme(13), "{:02x}", v.read_u8(addr).value);
|
||||
}
|
||||
else {
|
||||
fmt::print("{:02x}", v.read_u8(addr));
|
||||
fmt::print("{:02x}", v.read_u8(addr).value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
91
shell/i.cpp
91
shell/i.cpp
|
@ -34,11 +34,69 @@ void _ic(Session &session, struct _ic_args const &args)
|
|||
return;
|
||||
|
||||
for(uint32_t address: args.addresses) {
|
||||
fmt::print("Claim over 0x{:08x}:\n", address);
|
||||
Claim const *claim = session.current_space->disasm.getClaimAt(address);
|
||||
if(claim)
|
||||
fmt::print("0x{:08x} is claimed by {}\n", address, claim->str());
|
||||
fmt::print(" 0x{:08x} is claimed by {}\n", address, claim->str());
|
||||
else
|
||||
fmt::print("0x{:08x} is not claimed\n", address);
|
||||
fmt::print(" 0x{:08x} is not claimed\n", address);
|
||||
|
||||
auto dep = session.current_space->disasm.findClaimsOwnedBy(address);
|
||||
fmt::print("Claims owned by 0x{:08x}:\n", address);
|
||||
for(Claim const *c: dep)
|
||||
fmt::print(" - {}\n", c->str());
|
||||
if(!dep.size())
|
||||
fmt::print(" (none)\n");
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// if
|
||||
//---
|
||||
|
||||
struct _if_args
|
||||
{
|
||||
std::vector<uint32_t> 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,6 +307,7 @@ struct _is_args
|
|||
{
|
||||
std::string vspace_name;
|
||||
std::optional<FxOS::Symbol> symbol;
|
||||
bool sort;
|
||||
};
|
||||
|
||||
static struct _is_args parse_is(Session &session, Parser &parser)
|
||||
|
@ -258,6 +317,9 @@ static struct _is_args parse_is(Session &session, Parser &parser)
|
|||
parser.option("vspace",
|
||||
[&args](std::string const &value) { args.vspace_name = value; });
|
||||
|
||||
parser.option("sort",
|
||||
[&args](std::string const &value) { args.sort = (value == "true"); });
|
||||
|
||||
parser.accept_options();
|
||||
|
||||
FxOS::Symbol s;
|
||||
|
@ -296,7 +358,7 @@ static struct _is_args parse_is(Session &session, Parser &parser)
|
|||
}
|
||||
|
||||
void _is(Session &session, std::string vspace_name,
|
||||
std::optional<FxOS::Symbol> symbol)
|
||||
std::optional<FxOS::Symbol> symbol, bool sort)
|
||||
{
|
||||
VirtualSpace *space = session.current_space;
|
||||
if(!space) {
|
||||
|
@ -351,6 +413,9 @@ void _is(Session &session, std::string vspace_name,
|
|||
symbols = {s};
|
||||
}
|
||||
|
||||
if(sort)
|
||||
std::sort(&symbols[0], &symbols[symbols.size()]);
|
||||
|
||||
for(auto const &s: symbols) {
|
||||
if(s.type == FxOS::Symbol::Syscall) {
|
||||
fmt::print(theme(10), " %{:04x}", s.value);
|
||||
|
@ -377,6 +442,19 @@ 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 [<function>...]
|
||||
|
||||
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"(
|
||||
|
@ -404,12 +482,13 @@ static ShellCommand _is_cmd(
|
|||
"is",
|
||||
[](Session &s, Parser &p) {
|
||||
auto args = parse_is(s, p);
|
||||
_is(s, args.vspace_name, args.symbol);
|
||||
_is(s, args.vspace_name, args.symbol, args.sort);
|
||||
},
|
||||
[](Session &s, Parser &p) { parse_is(s, p); }, "Info Symbols", R"(
|
||||
is [vspace=<virtual_space>] [<address|syscall>]
|
||||
is [sort=true] [vspace=<virtual_space>] [<address|syscall>]
|
||||
|
||||
Lists symbols in the specified virtual space (defaults to the current
|
||||
one). By default, all symbols are listed, but if an address or syscall is
|
||||
provided, the symbol associated with it will be printed instead.
|
||||
provided, the symbol associated with it will be printed instead. If sort=true,
|
||||
symbols will be sorted by syscall number and then address.
|
||||
)");
|
||||
|
|
|
@ -424,7 +424,15 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
/* Exhaust command input (if not all used by the command) */
|
||||
parser.exhaust_until_separator();
|
||||
while(true) {
|
||||
try {
|
||||
parser.exhaust_until_separator();
|
||||
break;
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
FxOS_log(ERR, "%s", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Save command history */
|
||||
|
|
|
@ -142,24 +142,7 @@ void Parser::dump_command()
|
|||
{
|
||||
while(!at_end()) {
|
||||
Token t = m_la;
|
||||
|
||||
if(t.type == T::NUM)
|
||||
fmt::print("NUM {:#x}\n", t.value.NUM);
|
||||
else if(t.type == T::SYSCALL)
|
||||
fmt::print("SYSCALL %{:04x}\n", t.value.NUM);
|
||||
else if(t.type == T::SYMBOL)
|
||||
fmt::print("SYMBOL '{}'\n", t.value.STRING);
|
||||
else if(t.type == T::OPTION)
|
||||
fmt::print("OPTION '{}'\n", t.value.STRING);
|
||||
else if(t.type == T::STRING)
|
||||
fmt::print("STRING '{}'\n", t.value.STRING);
|
||||
else if(t.type == '>')
|
||||
fmt::print(">>\n");
|
||||
else if(t.type == '<')
|
||||
fmt::print("<<\n");
|
||||
else
|
||||
fmt::print("{}\n", (char)t.type);
|
||||
|
||||
fmt::print("{}\n", t.str());
|
||||
feed();
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +229,7 @@ Range Parser::range(VirtualSpace *space, long before, long after)
|
|||
{
|
||||
long start = expr(space);
|
||||
|
||||
/* Accept non-rangs if (before) and (after) are provided */
|
||||
/* Accept non-ranges if (before) and (after) are provided */
|
||||
if(m_la.type != ':' && m_la.type != '.' && before >= 0 && after >= 0)
|
||||
return {start - before, start + after};
|
||||
|
||||
|
@ -259,6 +242,25 @@ Range Parser::range(VirtualSpace *space, long before, long after)
|
|||
return r;
|
||||
}
|
||||
|
||||
std::variant<long, Range> Parser::expr_or_range(VirtualSpace *space)
|
||||
{
|
||||
long start = expr(space);
|
||||
|
||||
if(m_la.type == ':') {
|
||||
expect(':');
|
||||
long length = expr(space);
|
||||
return (Range) {start, start + length};
|
||||
}
|
||||
else if(m_la.type == '.') {
|
||||
expect('.');
|
||||
long end = expr(space);
|
||||
return (Range) {start, end};
|
||||
}
|
||||
else {
|
||||
return (long)start;
|
||||
}
|
||||
}
|
||||
|
||||
FxOS::MemoryRegion Parser::region(VirtualSpace *space, long before, long after)
|
||||
{
|
||||
if(m_la.type == '$' || m_la.type == '(' || m_la.type == '-'
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <variant>
|
||||
|
||||
#include <fxos/memory.h>
|
||||
#include <fxos/vspace.h>
|
||||
|
@ -144,8 +145,10 @@ public:
|
|||
long expr(VirtualSpace *space);
|
||||
/* Read a range; again $ and symbols are interpreted. If (before) and
|
||||
(after) are both specified, a single value will also be accepted, and
|
||||
the range [value+before, value.after) will be returned. */
|
||||
the range [value-before, value+after) will be returned. */
|
||||
Range range(VirtualSpace *space, long before=-1, long after=-1);
|
||||
/* Read an expression or a range */
|
||||
std::variant<long, Range> expr_or_range(VirtualSpace *space);
|
||||
/* Read a memory region (allows both ranges and symbolic names) */
|
||||
FxOS::MemoryRegion region(VirtualSpace *space, long before=-1,
|
||||
long after=-1);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <fxos/symbols.h>
|
||||
#include <fmt/core.h>
|
||||
#include <endian.h>
|
||||
|
||||
//---
|
||||
// sh
|
||||
|
|
Loading…
Reference in New Issue