forked from Lephenixnoir/fxos
implement the syscall pass, and symbol resolution
New features: * The syscall pass now resolves syscalls for the input target, provided that an OS is mapped on the ROM region. * Formalized the variations of print's arguments as a sequence (tree, to be precise) of /promotions/. * Added a short notion of Symbol and SymbolTable, and a loader for them. Data files of type "symbol" are read as such and provide name to syscalls or arbitrary addresses. Code changes: * The disassembly operation of the command-line interface is now finally in its own file with more room. * Encoded the tree structure of promotions as a sequence of (mainly tail-calling) inter-calling methods in the print pass.
This commit is contained in:
parent
08e26aee2e
commit
d5c5fa6aeb
2
Makefile
2
Makefile
|
@ -101,9 +101,7 @@ endif
|
|||
|
||||
install: $(TARGETS)
|
||||
install -d $(PREFIX)/bin
|
||||
install -d $(PREFIX)/share/fxos
|
||||
install $(TARGETS) $(m755) $(PREFIX)/bin
|
||||
cp -ra data/* $(PREFIX)/share/fxos
|
||||
|
||||
uninstall:
|
||||
rm -f $(TARGETS:%=$(PREFIX)/%)
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
type: types
|
||||
name: base
|
||||
---
|
||||
|
||||
u32 StatusRegister {
|
||||
u _ :1;
|
||||
u MD :1;
|
||||
u RB :1;
|
||||
u BL :1;
|
||||
u _ :20;
|
||||
u IMASK :4;
|
||||
u _ :3;
|
||||
u T :1;
|
||||
}
|
||||
|
||||
struct MountTableEntry {
|
||||
u32 _;
|
||||
u32 _;
|
||||
u32 _;
|
||||
char[20] path2;
|
||||
char[18] path1;
|
||||
u8 mounted;
|
||||
u8 _;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#include "fxos-cli.h"
|
||||
#include <fxos/disassembly.h>
|
||||
#include <fxos/memory.h>
|
||||
#include <fxos/target.h>
|
||||
#include <fxos/util.h>
|
||||
#include <fxos/log.h>
|
||||
#include <fxos/os.h>
|
||||
|
||||
#include <fxos/disasm-passes/cfg.h>
|
||||
#include <fxos/disasm-passes/pcrel.h>
|
||||
#include <fxos/disasm-passes/syscall.h>
|
||||
#include <fxos/disasm-passes/print.h>
|
||||
|
||||
using namespace FxOS;
|
||||
using namespace FxOS::Log;
|
||||
|
||||
void disassembly(Library &library, Target &target, uint32_t ref,
|
||||
std::vector<std::string> passes)
|
||||
{
|
||||
Disassembly disasm(target);
|
||||
|
||||
/* Observe the target only if it has an OS mapped */
|
||||
std::unique_ptr<OS> os;
|
||||
if(target.covers(MemoryRegion::ROM))
|
||||
os = std::make_unique<OS>(target);
|
||||
|
||||
for(auto pass: passes)
|
||||
{
|
||||
auto start = timer_start();
|
||||
log(LOG "Running pass %s...\\", pass);
|
||||
|
||||
if(pass == "cfg")
|
||||
{
|
||||
CfgPass p(disasm);
|
||||
p.run(ref);
|
||||
}
|
||||
else if(pass == "pcrel")
|
||||
{
|
||||
PcrelPass p(disasm);
|
||||
p.run(ref);
|
||||
}
|
||||
else if(pass == "syscall")
|
||||
{
|
||||
SyscallPass p(disasm, os.get());
|
||||
p.run(ref);
|
||||
}
|
||||
else if(pass == "print")
|
||||
{
|
||||
PrintPass p(disasm, library.sym_tables());
|
||||
|
||||
p.promote_pcjump_loc = PrintPass::Promote;
|
||||
p.promote_pcrel_loc = PrintPass::Promote;
|
||||
p.promote_pcrel_value = PrintPass::Promote;
|
||||
p.promote_syscall = PrintPass::Promote;
|
||||
p.promote_syscallname = PrintPass::Append;
|
||||
p.promote_symbol = PrintPass::Append;
|
||||
|
||||
p.run();
|
||||
}
|
||||
log(LOG "%s", timer_format(timer_end(start)));
|
||||
}
|
||||
}
|
|
@ -6,9 +6,15 @@
|
|||
#define FXOS_CLI_H
|
||||
|
||||
#include <fxos/target.h>
|
||||
#include <fxos/library.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/* Print general information on an OS file */
|
||||
void os_info(FxOS::Target &target);
|
||||
|
||||
/* Disassemble */
|
||||
void disassembly(FxOS::Library &library, FxOS::Target &target, uint32_t ref,
|
||||
std::vector<std::string> passes);
|
||||
|
||||
#endif /* FXOS_CLI_H */
|
||||
|
|
|
@ -8,16 +8,12 @@
|
|||
#include <fxos/log.h>
|
||||
#include <fxos/os.h>
|
||||
|
||||
#include <fxos/disasm-passes/cfg.h>
|
||||
#include <fxos/disasm-passes/pcrel.h>
|
||||
#include <fxos/disasm-passes/print.h>
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
@ -80,11 +76,10 @@ Available passes:
|
|||
pcrel Resolve PC-relative references as their target address
|
||||
cstprop Propagate constants by abstract interpretation
|
||||
syscall Annotate code with reverse syscalls
|
||||
regs Annotate code with peripheral register addresses
|
||||
|
||||
The default sequence of passes is cfg,pcrel,cstprop,syscall,regs. When
|
||||
disassembling a function (ie. no size specified on the command-line), the cfg
|
||||
pass is always executed to explore the function.
|
||||
The default sequence of passes is cfg,pcrel,cstprop,syscall. When disassembling
|
||||
a function (ie. no size specified on the command-line), the cfg pass is always
|
||||
executed to explore the function.
|
||||
|
||||
ANALYZE COMMAND
|
||||
|
||||
|
@ -221,9 +216,10 @@ int main_info(int argc, char **argv)
|
|||
|
||||
int main_disassembly(int argc, char **argv)
|
||||
{
|
||||
int error=0, option=0, mpu='4';
|
||||
int error=0, option=0;
|
||||
__attribute__((unused)) int mpu='4';
|
||||
std::vector<std::string> passes {
|
||||
"cfg", "pcrel", "constprop", "syscall", "regs", "print"
|
||||
"cfg", "pcrel", "constprop", "syscall", "print"
|
||||
};
|
||||
std::string file;
|
||||
|
||||
|
@ -304,39 +300,11 @@ int main_disassembly(int argc, char **argv)
|
|||
uint32_t ref;
|
||||
sscanf(refstr, "%x", &ref);
|
||||
|
||||
Disassembly disasm(target);
|
||||
OS *os = nullptr;
|
||||
|
||||
log(LOG "Disassembling target %s at %s", tname, refstr);
|
||||
|
||||
try
|
||||
{
|
||||
for(auto pass: passes)
|
||||
{
|
||||
auto start = timer_start();
|
||||
log(LOG "Running pass %s...\\", pass);
|
||||
|
||||
if(pass == "cfg")
|
||||
{
|
||||
CfgPass p(disasm);
|
||||
p.run(ref);
|
||||
}
|
||||
else if(pass == "pcrel")
|
||||
{
|
||||
PcrelPass p(disasm);
|
||||
p.run(ref);
|
||||
}
|
||||
else if(pass == "print")
|
||||
{
|
||||
PrintPass p(disasm);
|
||||
p.hide_resolved_pcjump = true;
|
||||
p.hide_resolved_pcrel = true;
|
||||
p.hide_movpc_address =
|
||||
PrintPass::Hide_MovPC_Region;
|
||||
p.run();
|
||||
}
|
||||
log(LOG "%s", timer_format(timer_end(start)));
|
||||
}
|
||||
disassembly(lib, target, ref, passes);
|
||||
}
|
||||
catch(LangError &e)
|
||||
{
|
||||
|
|
|
@ -9,13 +9,15 @@
|
|||
#define LIBFXOS_DISASM_PASSES_PRINT_H
|
||||
|
||||
#include <fxos/disassembly.h>
|
||||
#include <fxos/symbols.h>
|
||||
|
||||
namespace FxOS {
|
||||
|
||||
class PrintPass: public DisassemblyPass
|
||||
{
|
||||
public:
|
||||
PrintPass(Disassembly &disasm);
|
||||
PrintPass(Disassembly &disasm,
|
||||
std::vector<SymbolTable> const &symtables);
|
||||
void analyze(uint32_t pc, ConcreteInstruction &inst) override;
|
||||
|
||||
/* This pass uses another entry method that starts at the instruction
|
||||
|
@ -27,31 +29,54 @@ public:
|
|||
// Print pass parameters
|
||||
//---
|
||||
|
||||
/* In jump instructions, hide the raw value "pc+<disp>" if the target
|
||||
address has been computed */
|
||||
bool hide_resolved_pcjump;
|
||||
/* In PC-relative move instructions, hide the raw value "@(<disp>,pc)"
|
||||
of the argument if the target address has been computed */
|
||||
bool hide_resolved_pcrel;
|
||||
/* Promotion parameters. Default is always to append. */
|
||||
enum Promotion {
|
||||
/* Never promote */
|
||||
Never=1,
|
||||
/* Promote but keep the lower-level information */
|
||||
Append=0,
|
||||
/* Promove and hide the lower-level information */
|
||||
Promote=2,
|
||||
};
|
||||
|
||||
/* In PC-relative move instructions, hide the target address of the
|
||||
argument... under conditions */
|
||||
enum {
|
||||
/* Always show the accessed address */
|
||||
Hide_MovPC_Never,
|
||||
/* Hide the address if it's in the same memory region as the
|
||||
instruction doing the move */
|
||||
Hide_MovPC_Region,
|
||||
/* Always hide the address */
|
||||
Hide_MovPC_Always,
|
||||
/** In the following, promote_x always means promote *to x* **/
|
||||
|
||||
} hide_movpc_address;
|
||||
/* In jumps, promote "pc+<disp>" to the target address */
|
||||
int promote_pcjump_loc;
|
||||
/* In a PC-relative mov, promote "@(<disp>,pc)" to computed address */
|
||||
int promote_pcrel_loc;
|
||||
/* In a PC-relative mov, promote address to pointed value */
|
||||
int promote_pcrel_value;
|
||||
/* Promote an integer to a syscall number */
|
||||
int promote_syscall;
|
||||
/* Promote a syscall number to a syscall name */
|
||||
int promote_syscallname;
|
||||
/* Promote an integer to a symbol */
|
||||
int promote_symbol;
|
||||
|
||||
/* TODO: More print pass parameters */
|
||||
|
||||
private:
|
||||
void pcrel(uint32_t pc, Argument const &a, Location const &l,
|
||||
std::optional<DataValue> v);
|
||||
/* Symbol tables to look up names */
|
||||
std::vector<SymbolTable> const &m_symtables;
|
||||
|
||||
/* Query symbol tables, most recent first */
|
||||
std::optional<std::string> symquery(Symbol::Type type, uint32_t value);
|
||||
|
||||
/** Internal promotion printing stuff **/
|
||||
|
||||
void queue(std::string, bool = false);
|
||||
void queue_flush();
|
||||
std::vector<std::string> m_messages;
|
||||
|
||||
void pcjumploc(ConcreteInstructionArg const &);
|
||||
void pcrelloc(ConcreteInstructionArg const &);
|
||||
void pcrelval(ConcreteInstructionArg const &);
|
||||
void syscall(ConcreteInstructionArg const &);
|
||||
void syscallname(ConcreteInstructionArg const &);
|
||||
void symbol(ConcreteInstructionArg const &);
|
||||
|
||||
/* To be removed */
|
||||
};
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
//---
|
||||
// fxos.disasm-passes.syscall: Detection and substitution of syscall addresses
|
||||
//
|
||||
// This passes looks for insruction arguments that evaluate to syscall
|
||||
// addresses, and substitutes to that the syscall number and (hopefully) the
|
||||
// syscall name will be shown by the print pass if it's available in the
|
||||
// documentation.
|
||||
//---
|
||||
|
||||
#ifndef FXOS_DISASM_PASSES_SYSCALL_H
|
||||
#define FXOS_DISASM_PASSES_SYSCALL_H
|
||||
|
||||
#include <fxos/disassembly.h>
|
||||
#include <fxos/os.h>
|
||||
|
||||
namespace FxOS {
|
||||
|
||||
class SyscallPass: public DisassemblyPass
|
||||
{
|
||||
public:
|
||||
SyscallPass(Disassembly &disasm, OS *os);
|
||||
|
||||
void analyze(uint32_t pc, ConcreteInstruction &inst) override;
|
||||
|
||||
private:
|
||||
OS *m_os;
|
||||
};
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
||||
#endif /* FXOS_DISASM_PASSES_SYSCALL_H */
|
|
@ -30,7 +30,7 @@ struct ConcreteInstructionArg
|
|||
ConcreteInstructionArg();
|
||||
|
||||
//---
|
||||
// Data set by the <pcrel> pass and abstract interpretater
|
||||
// Data set by the <pcrel> pass and abstract interpreter
|
||||
//---
|
||||
|
||||
/* Location in CPU or memory, if that can be determined */
|
||||
|
@ -42,13 +42,11 @@ struct ConcreteInstructionArg
|
|||
DataValue value;
|
||||
|
||||
//---
|
||||
// Data set by the <syscall> and <regs> passes
|
||||
// Data set by the <syscall> pass
|
||||
//---
|
||||
|
||||
/* If the value is a syscall address, the syscall's id */
|
||||
int syscall_id;
|
||||
/* If the value is a peripheral register, its address */
|
||||
uint32_t reg_address;
|
||||
};
|
||||
|
||||
/* A loaded and annotated instruction. */
|
||||
|
|
|
@ -21,9 +21,10 @@ public:
|
|||
|
||||
/* Construct abstract value from integer constant */
|
||||
virtual T constant(uint32_t value) const noexcept = 0;
|
||||
|
||||
/* Check if value is constant */
|
||||
virtual bool is_constant(T) const noexcept = 0;
|
||||
/* Unpack a constant */
|
||||
virtual uint32_t constant_value(T) const = 0;
|
||||
|
||||
/* Basic arithmetic. Division and modulo are both non-trivial
|
||||
instruction sequences usually isolated in easily-identifiable
|
||||
|
@ -130,6 +131,7 @@ public:
|
|||
|
||||
RelConst constant(uint32_t value) const noexcept override;
|
||||
bool is_constant(RelConst) const noexcept override;
|
||||
uint32_t constant_value(RelConst) const override;
|
||||
|
||||
RelConst minus(RelConst) const noexcept override;
|
||||
RelConst add(RelConst, RelConst) const noexcept override;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define FXOS_LIBRARY_H
|
||||
|
||||
#include <fxos/target.h>
|
||||
#include <fxos/symbols.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -39,11 +40,16 @@ struct Library
|
|||
const std::vector<std::string> &asm_tables() {
|
||||
return m_asmtables;
|
||||
}
|
||||
/* List of symbol tables */
|
||||
const std::vector<SymbolTable> &sym_tables() {
|
||||
return m_symtables;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_paths;
|
||||
std::map<std::string, TargetDescription> m_targets;
|
||||
std::vector<std::string> m_asmtables;
|
||||
std::vector<SymbolTable> m_symtables;
|
||||
};
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <fxos/util.h>
|
||||
#include <fxos/target.h>
|
||||
#include <fxos/symbols.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
@ -41,6 +42,10 @@ void load_asm(Buffer const &file, size_t offset, size_t line);
|
|||
the complete target description */
|
||||
TargetDescription load_target(Buffer const &file, size_t offset, size_t line);
|
||||
|
||||
/* Load a symbol table. This function returns the full table, which may contain
|
||||
duplicates or unused syscall numbers and addresses. */
|
||||
SymbolTable load_symbols(Buffer const &file, size_t offset, size_t line);
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
||||
#endif /* LIBFXOS_LOAD_H */
|
||||
|
|
|
@ -85,7 +85,7 @@ struct StringType: public BaseType
|
|||
bool nul_terminated;
|
||||
};
|
||||
|
||||
/* Heterogenous structure types. */
|
||||
/* Heterogeneous structure types. */
|
||||
struct StructType: public BaseType
|
||||
{
|
||||
/* Fields can be of any type since all are fixed-size. */
|
||||
|
@ -143,17 +143,20 @@ struct DataValue
|
|||
DataType const *type;
|
||||
std::vector<int16_t> mem;
|
||||
|
||||
/* Create value with no memory and no tyê */
|
||||
/* Create value with no memory and no type */
|
||||
DataValue();
|
||||
/* Create value with uninitialized memory for that data type */
|
||||
DataValue(DataType const *type);
|
||||
|
||||
/* Check whether the value is fully defined and initialized */
|
||||
bool defined() {
|
||||
bool defined() const {
|
||||
return std::find(mem.begin(), mem.end(), -1) == mem.end();
|
||||
}
|
||||
operator bool() const {
|
||||
return defined();
|
||||
}
|
||||
|
||||
/* Checks that the access is correct and fits witin the value. */
|
||||
/* Checks that the access is correct and fits within the value. */
|
||||
void access(size_t offset, size_t size) const;
|
||||
/* Read data from the value. Access must be 1, 2 or 4 bytes (possibly
|
||||
unaligned) and must be in bounds. */
|
||||
|
@ -161,6 +164,9 @@ struct DataValue
|
|||
/* Write data. Access must be 1, 2 or 4 bytes and in bounds. */
|
||||
void write(size_t offset, size_t size, uint32_t contents);
|
||||
|
||||
/* Retrieve value as uin32_t - only valid for Integer types */
|
||||
uint32_t uinteger() const;
|
||||
|
||||
/* Byte-based string representation */
|
||||
std::string str() const noexcept;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
//---
|
||||
// fxos.symbols: User-defined symbols for OS objects
|
||||
//---
|
||||
|
||||
#ifndef LIBFXOS_SYMBOLS_H
|
||||
#define LIBFXOS_SYMBOLS_H
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace FxOS
|
||||
{
|
||||
|
||||
/* A named symbol that can be substituted to literal values in the code. */
|
||||
struct Symbol
|
||||
{
|
||||
/* Syscall: The value is a syscall number. The syscall number for an
|
||||
address is determined by querying the OS object.
|
||||
Address: The value is a fixed 32-bit virtual address. */
|
||||
enum Type { Syscall=1, Address=2 };
|
||||
|
||||
enum Type type;
|
||||
uint32_t value;
|
||||
|
||||
/* Symbol name, no particular conventions */
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/* A symbol table, essentially a set of symbols loaded from the same file */
|
||||
struct SymbolTable
|
||||
{
|
||||
std::string table_name;
|
||||
std::vector<Symbol> symbols;
|
||||
|
||||
/* Add a symbol to the table */
|
||||
void add(Symbol s);
|
||||
/* Query a value for a certain type of symbol */
|
||||
std::optional<std::string> query(Symbol::Type type, uint32_t value)
|
||||
const;
|
||||
};
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
||||
#endif /* LIBFXOS_SYMBOLS_H */
|
|
@ -20,6 +20,8 @@ class AbstractMemory
|
|||
public:
|
||||
/* Checks if an address or interval is simulated */
|
||||
virtual bool covers(uint32_t addr, int size=1) const noexcept = 0;
|
||||
/* Check if a full region is simulated */
|
||||
virtual bool covers(MemoryRegion const ®ion) const noexcept;
|
||||
|
||||
/* Returns the data located at the provided virtual address. Throws
|
||||
std::out_of_range if the interval is not entirely simulated */
|
||||
|
@ -75,6 +77,8 @@ struct Binding: public AbstractMemory
|
|||
|
||||
/* Checks if an address is covered by the binding */
|
||||
bool covers(uint32_t addr, int size=1) const noexcept override;
|
||||
/* Check if a region is covered by the binding */
|
||||
bool covers(MemoryRegion const ®ion) const noexcept override;
|
||||
|
||||
/* Returns this process' address (in [data]) corresponding to the
|
||||
provided virtual address */
|
||||
|
@ -118,6 +122,8 @@ public:
|
|||
|
||||
/* Check if an address is bound */
|
||||
bool covers(uint32_t addr, int size=1) const noexcept override;
|
||||
/* Check if a full region is bound */
|
||||
bool covers(MemoryRegion const ®ion) const noexcept override;
|
||||
|
||||
/* Returns the data at the provided virtual address */
|
||||
char const *translate(uint32_t addr, int size=1) const override;
|
||||
|
|
|
@ -27,7 +27,7 @@ void register_instruction(Instruction ins)
|
|||
//---
|
||||
|
||||
ConcreteInstructionArg::ConcreteInstructionArg():
|
||||
value(), syscall_id(-1), reg_address((uint32_t)-1)
|
||||
value(), syscall_id(-1)
|
||||
{
|
||||
location = RelConstDomain().bottom();
|
||||
}
|
||||
|
|
|
@ -44,6 +44,13 @@ bool RelConstDomain::is_constant(RelConst r) const noexcept
|
|||
return r.base == 0;
|
||||
}
|
||||
|
||||
uint32_t RelConstDomain::constant_value(RelConst r) const
|
||||
{
|
||||
if(!is_constant(r))
|
||||
throw std::invalid_argument("Not a constant RelConst");
|
||||
return r.uval;
|
||||
}
|
||||
|
||||
//---
|
||||
// Basic arithmetic
|
||||
//---
|
||||
|
|
|
@ -83,6 +83,16 @@ void Library::load(std::string path)
|
|||
log(ERR "%s", e.str());
|
||||
}
|
||||
}
|
||||
else if(type == "symbols")
|
||||
{
|
||||
try {
|
||||
SymbolTable st = load_symbols(file, offset, line);
|
||||
m_symtables.push_back(st);
|
||||
}
|
||||
catch(FxOS::SyntaxError &e) {
|
||||
log(ERR "%s", e.str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log(ERR "unknown file type '%s' in '%s'", type, path);
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
%{
|
||||
#include <fxos/symbols.h>
|
||||
#include <fxos/errors.h>
|
||||
#include <fxos/util.h>
|
||||
#include <fxos/load.h>
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
/* Text value and integer value for parser */
|
||||
static char *yylval;
|
||||
uint32_t yyival;
|
||||
|
||||
/* Tokens */
|
||||
#define SYSCALL 1
|
||||
#define ADDRESS 2
|
||||
#define NAME 3
|
||||
|
||||
/* Current file name */
|
||||
static std::string filename;
|
||||
|
||||
/* Error messages and exceptions */
|
||||
static void err(char const *format, ...)
|
||||
{
|
||||
static char buf[256];
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vsnprintf(buf, 256, format, args);
|
||||
va_end(args);
|
||||
|
||||
throw FxOS::SyntaxError(filename.c_str(), yylineno, buf);
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%option prefix="symbols"
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
|
||||
syscall ^%[0-9A-Fa-f]{3,}
|
||||
address ^[0-9A-Fa-f]{8}
|
||||
name [a-zA-Z_][a-zA-Z_0-9.]*
|
||||
space [ \t]+
|
||||
|
||||
%%
|
||||
|
||||
^#[^\n]* ;
|
||||
{space} ;
|
||||
[\n] yylineno++;
|
||||
|
||||
{syscall} { yyival = strtol(yytext+1, NULL, 16); return SYSCALL; }
|
||||
{address} { yyival = strtol(yytext, NULL, 16); return ADDRESS; }
|
||||
{name} { yylval = strdup(yytext); return NAME; }
|
||||
|
||||
. { err("lex error near '%s'", yytext); }
|
||||
<<EOF>> { return -1; }
|
||||
|
||||
%%
|
||||
|
||||
namespace FxOS {
|
||||
|
||||
/* Load a symbol table into the disassembler */
|
||||
SymbolTable load_symbols(Buffer const &file, size_t start_offset,
|
||||
size_t start_line)
|
||||
{
|
||||
YY_BUFFER_STATE buf = yy_scan_bytes(file.data + start_offset,
|
||||
file.size - start_offset);
|
||||
yylineno = start_line;
|
||||
filename = file.path;
|
||||
|
||||
SymbolTable table;
|
||||
|
||||
/* Current symbol and line */
|
||||
Symbol symbol;
|
||||
int line = -1;
|
||||
|
||||
while(1)
|
||||
{
|
||||
int t = yylex();
|
||||
|
||||
if(line >= 0 && (yylineno != line || t != NAME || t == -1))
|
||||
{
|
||||
/* Finalize current symbol */
|
||||
if(symbol.name == "") err("%d: missing name", line);
|
||||
else table.add(symbol);
|
||||
|
||||
symbol = Symbol();
|
||||
}
|
||||
if(t == -1) break;
|
||||
|
||||
if(t == SYSCALL)
|
||||
{
|
||||
symbol.type = Symbol::Syscall;
|
||||
symbol.value = yyival;
|
||||
line = yylineno;
|
||||
}
|
||||
else if(t == ADDRESS)
|
||||
{
|
||||
symbol.type = Symbol::Address;
|
||||
symbol.value = yyival;
|
||||
line = yylineno;
|
||||
}
|
||||
else if(t == NAME)
|
||||
{
|
||||
symbol.name = yylval;
|
||||
free(yylval);
|
||||
}
|
||||
}
|
||||
|
||||
yy_delete_buffer(buf);
|
||||
return table;
|
||||
}
|
||||
|
||||
} /* namespace FxOS */
|
|
@ -44,12 +44,15 @@ void PcrelPass::analyze(uint32_t pc, ConcreteInstruction &ci)
|
|||
}
|
||||
|
||||
ca.value = DataValue(IntegerType::u32);
|
||||
ca.value.write(0,4,v);
|
||||
ca.value.write(0, 4, v);
|
||||
}
|
||||
else if(a.kind == Argument::PcJump)
|
||||
{
|
||||
uint32_t addr = pc + 4 + a.disp;
|
||||
ca.location = RelConstDomain().constant(addr);
|
||||
|
||||
ca.value = DataValue(IntegerType::u32);
|
||||
ca.value.write(0, 4, addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,15 +5,16 @@
|
|||
#include <fxos/disasm-passes/print.h>
|
||||
#include <fxos/disassembly.h>
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
namespace FxOS {
|
||||
|
||||
PrintPass::PrintPass(Disassembly &disasm):
|
||||
DisassemblyPass(disasm)
|
||||
PrintPass::PrintPass(Disassembly &disasm,
|
||||
std::vector<SymbolTable> const &symtables):
|
||||
DisassemblyPass(disasm), m_symtables(symtables)
|
||||
{
|
||||
/* Default parameter set */
|
||||
hide_resolved_pcjump = false;
|
||||
hide_resolved_pcrel = false;
|
||||
hide_movpc_address = Hide_MovPC_Never;
|
||||
/* All 0 */
|
||||
}
|
||||
|
||||
void PrintPass::run(void)
|
||||
|
@ -43,59 +44,124 @@ void PrintPass::analyze(uint32_t pc, ConcreteInstruction &ci)
|
|||
|
||||
for(size_t n = 0; n < i.args.size(); n++)
|
||||
{
|
||||
auto &a = i.args[n];
|
||||
Location &l = ci.args[n].location;
|
||||
std::optional<DataValue> v = ci.args[n].value;
|
||||
Argument const &a = i.args[n];
|
||||
ConcreteInstructionArg const &arg = ci.args[n];
|
||||
|
||||
if(n) printf(", ");
|
||||
|
||||
if(a.kind == Argument::PcJump)
|
||||
{
|
||||
if(!l || !hide_resolved_pcjump)
|
||||
printf("%s", a.str().c_str());
|
||||
if(l)
|
||||
printf("<%s>", l.str().c_str());
|
||||
queue(a.str());
|
||||
pcjumploc(arg);
|
||||
queue_flush();
|
||||
}
|
||||
else if(a.kind == Argument::PcRel)
|
||||
{
|
||||
pcrel(pc, a, l, v);
|
||||
queue(a.str());
|
||||
pcrelloc(arg);
|
||||
queue_flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%s", a.str().c_str());
|
||||
queue(a.str());
|
||||
queue_flush();
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void PrintPass::pcrel(uint32_t pc, Argument const &a, Location const &l,
|
||||
std::optional<DataValue> v)
|
||||
std::optional<std::string> PrintPass::symquery(Symbol::Type type,
|
||||
uint32_t value)
|
||||
{
|
||||
if(!l || !hide_resolved_pcrel)
|
||||
for(int i = m_symtables.size() - 1; i >= 0; i--)
|
||||
{
|
||||
printf("%s", a.str().c_str());
|
||||
auto maybe_str = m_symtables[i].query(type, value);
|
||||
if(maybe_str) return maybe_str;
|
||||
}
|
||||
if(!l || !RelConstDomain().is_constant(l)) return;
|
||||
|
||||
auto reg_code = MemoryRegion::region_for(pc);
|
||||
auto reg_data = MemoryRegion::region_for(l.uval);
|
||||
|
||||
bool hma = hide_movpc_address;
|
||||
bool same_region = (reg_code && reg_code == reg_data);
|
||||
|
||||
if(!v || hma == Hide_MovPC_Never ||
|
||||
(hma == Hide_MovPC_Region && !same_region))
|
||||
{
|
||||
printf("<%s>", l.str().c_str());
|
||||
if(v)
|
||||
printf("(%s)", v->str().c_str());
|
||||
}
|
||||
else if(v)
|
||||
{
|
||||
printf("%s", v->str().c_str());
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void PrintPass::queue(std::string str, bool override)
|
||||
{
|
||||
if(override && m_messages.size())
|
||||
m_messages.pop_back();
|
||||
|
||||
m_messages.push_back(str);
|
||||
}
|
||||
|
||||
void PrintPass::queue_flush()
|
||||
{
|
||||
for(size_t i = 0; i < m_messages.size(); i++)
|
||||
{
|
||||
if(i != 0) printf(" ");
|
||||
printf("%s", m_messages[i].c_str());
|
||||
}
|
||||
|
||||
m_messages.clear();
|
||||
}
|
||||
|
||||
void PrintPass::pcjumploc(ConcreteInstructionArg const &arg)
|
||||
{
|
||||
if(!RelConstDomain().is_constant(arg.location)) return;
|
||||
if(promote_pcjump_loc == Never) return;
|
||||
|
||||
queue(format("<%s>", arg.location.str()), promote_pcjump_loc==Promote);
|
||||
syscall(arg);
|
||||
}
|
||||
|
||||
void PrintPass::pcrelloc(ConcreteInstructionArg const &arg)
|
||||
{
|
||||
if(!RelConstDomain().is_constant(arg.location)) return;
|
||||
if(promote_pcrel_loc == Never) return;
|
||||
|
||||
queue(format("<%s>", arg.location.str()), promote_pcrel_loc==Promote);
|
||||
pcrelval(arg);
|
||||
}
|
||||
|
||||
void PrintPass::pcrelval(ConcreteInstructionArg const &arg)
|
||||
{
|
||||
if(!arg.value || arg.value.type->kind() != DataType::Integer) return;
|
||||
if(promote_pcrel_value == Never) return;
|
||||
|
||||
queue(arg.value.str(), promote_pcrel_value==Promote);
|
||||
syscall(arg);
|
||||
}
|
||||
|
||||
void PrintPass::syscall(ConcreteInstructionArg const &arg)
|
||||
{
|
||||
if(!arg.value || arg.value.type->kind() != DataType::Integer) return;
|
||||
|
||||
/* If this is not a syscall, try to display as a symbol instead */
|
||||
if(promote_syscall == Never || arg.syscall_id < 0)
|
||||
{
|
||||
symbol(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
queue(format("%%%03x", arg.syscall_id), promote_syscall==Promote);
|
||||
syscallname(arg);
|
||||
}
|
||||
|
||||
void PrintPass::syscallname(ConcreteInstructionArg const &arg)
|
||||
{
|
||||
if(arg.syscall_id < 0) return;
|
||||
|
||||
auto maybe_name = symquery(Symbol::Syscall, arg.syscall_id);
|
||||
if(!maybe_name) return;
|
||||
|
||||
queue(*maybe_name, promote_syscallname==Promote);
|
||||
}
|
||||
|
||||
void PrintPass::symbol(ConcreteInstructionArg const &arg)
|
||||
{
|
||||
if(!arg.value || arg.value.type->kind() != DataType::Integer) return;
|
||||
|
||||
auto maybe_name = symquery(Symbol::Address, arg.value.uinteger());
|
||||
if(!maybe_name) return;
|
||||
|
||||
queue(*maybe_name, promote_symbol==Promote);
|
||||
}
|
||||
|
||||
} /* namespace FxOS */
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
//---
|
||||
// fxos.passes.syscall: Detection and substitution of syscall addresses
|
||||
//---
|
||||
|
||||
#include <fxos/disasm-passes/syscall.h>
|
||||
|
||||
namespace FxOS {
|
||||
|
||||
SyscallPass::SyscallPass(Disassembly &disasm, OS *os):
|
||||
DisassemblyPass(disasm), m_os(os)
|
||||
{
|
||||
}
|
||||
|
||||
void SyscallPass::analyze(uint32_t pc, ConcreteInstruction &ci)
|
||||
{
|
||||
/* Nothing to do if no syscall table is provided! */
|
||||
if(!m_os) return;
|
||||
|
||||
Instruction const &i = ci.inst;
|
||||
|
||||
for(size_t n = 0; n < i.args.size(); n++)
|
||||
{
|
||||
Argument const &a = i.args[n];
|
||||
ConcreteInstructionArg &ca = ci.args[n];
|
||||
|
||||
bool eligible = false;
|
||||
uint32_t address;
|
||||
|
||||
if(a.kind == Argument::PcRel && ca.value
|
||||
&& ca.value.type == IntegerType::u32)
|
||||
{
|
||||
eligible = true;
|
||||
address = ca.value.read(0, 4);
|
||||
}
|
||||
if(a.kind == Argument::PcJump && ca.location
|
||||
&& RelConstDomain().is_constant(ca.location))
|
||||
{
|
||||
eligible = true;
|
||||
address = RelConstDomain().constant_value(ca.location);
|
||||
}
|
||||
|
||||
if(eligible)
|
||||
{
|
||||
int sid = m_os->find_syscall(address);
|
||||
if(sid >= 0) ca.syscall_id = sid;
|
||||
}
|
||||
}
|
||||
|
||||
enqueue_unseen_successors(pc, ci);
|
||||
}
|
||||
|
||||
} /* namespace FxOS */
|
|
@ -124,6 +124,14 @@ void DataValue::write(size_t offset, size_t size, uint32_t contents)
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t DataValue::uinteger() const
|
||||
{
|
||||
if(!type || type->kind() != DataType::Integer)
|
||||
throw std::logic_error("uinteger() on non-int DataValue");
|
||||
|
||||
return read(0, type->size());
|
||||
}
|
||||
|
||||
std::string DataValue::str() const noexcept
|
||||
{
|
||||
std::string result;
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
#include <fxos/symbols.h>
|
||||
|
||||
namespace FxOS {
|
||||
|
||||
void SymbolTable::add(Symbol s)
|
||||
{
|
||||
symbols.push_back(s);
|
||||
}
|
||||
|
||||
std::optional<std::string> SymbolTable::query(Symbol::Type type,
|
||||
uint32_t value) const
|
||||
{
|
||||
for(auto &sym: symbols)
|
||||
{
|
||||
if(sym.type == type && sym.value == value) return sym.name;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} /* namespace FxOS */
|
|
@ -9,6 +9,11 @@ namespace FxOS {
|
|||
// Simulated memory access primitives
|
||||
//---
|
||||
|
||||
bool AbstractMemory::covers(MemoryRegion const ®ion) const noexcept
|
||||
{
|
||||
return covers(region.start, region.size());
|
||||
}
|
||||
|
||||
Addressable<int8_t> AbstractMemory::read_i8(uint32_t addr) const
|
||||
{
|
||||
int8_t *i8 = (int8_t *)translate(addr, 1);
|
||||
|
@ -74,6 +79,11 @@ bool Binding::covers(uint32_t addr, int size) const noexcept
|
|||
return addr >= region.start && addr + size <= region.end;
|
||||
}
|
||||
|
||||
bool Binding::covers(MemoryRegion const ®ion) const noexcept
|
||||
{
|
||||
return covers(region.start, region.size());
|
||||
}
|
||||
|
||||
char const *Binding::translate(uint32_t addr, int size) const
|
||||
{
|
||||
if(!covers(addr, size))
|
||||
|
@ -140,6 +150,11 @@ bool Target::covers(uint32_t addr, int size) const noexcept
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Target::covers(MemoryRegion const ®ion) const noexcept
|
||||
{
|
||||
return covers(region.start, region.size());
|
||||
}
|
||||
|
||||
char const *Target::translate(uint32_t addr, int size) const
|
||||
{
|
||||
for(auto it = m_bindings.crbegin(); it != m_bindings.crend(); it++)
|
||||
|
|
Loading…
Reference in New Issue