277 lines
7.9 KiB
C++
277 lines
7.9 KiB
C++
//---------------------------------------------------------------------------//
|
|
// 1100101 |_ mov #0, r4 __ //
|
|
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
|
|
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
|
|
// |_ base# + offset |_| /_\_\___/__/ //
|
|
//---------------------------------------------------------------------------//
|
|
// fxos/binary: Main object for working with programs
|
|
//
|
|
// This header defines the main structures that combine to form the Binary
|
|
// object, which is the main access point for analyzing a program. The Binary
|
|
// structure references a VirtualSpace which emulates memory and can be used to
|
|
// read raw data. It then lists every Variable and Function that has been
|
|
// marked out in the program, either manually or by an analysis.
|
|
//---
|
|
|
|
#ifndef FXOS_BINARY_H
|
|
#define FXOS_BINARY_H
|
|
|
|
#include <fxos/util/types.h>
|
|
#include <fxos/util/bson.h>
|
|
#include <fxos/vspace.h>
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <ranges>
|
|
#include <map>
|
|
|
|
namespace FxOS {
|
|
|
|
class OS;
|
|
struct BinaryObject;
|
|
struct Mark;
|
|
struct Variable;
|
|
struct Function;
|
|
|
|
/* Base structure for all /binary objets/, ie. program objects that can be
|
|
declared in the program space. */
|
|
struct BinaryObject
|
|
{
|
|
enum Type : u8 { Mark, Variable, Function };
|
|
|
|
BinaryObject(Binary &binary, Type type, u32 address, u32 size):
|
|
m_binary {binary}, m_address {address}, m_size {size}, m_type {type}
|
|
{
|
|
}
|
|
|
|
/* Location and size in the address space. */
|
|
u32 address() const
|
|
{
|
|
return m_address;
|
|
}
|
|
u32 size() const
|
|
{
|
|
return m_size;
|
|
}
|
|
void setSize(u32 size)
|
|
{
|
|
m_size = size;
|
|
}
|
|
|
|
/* Binary that owns the object. */
|
|
Binary &parentBinary()
|
|
{
|
|
return m_binary;
|
|
}
|
|
Binary const &parentBinary() const
|
|
{
|
|
return m_binary;
|
|
}
|
|
|
|
/* Symbol name, no requirements on uniqueness or character set. */
|
|
std::string const &name() const
|
|
{
|
|
return m_name;
|
|
}
|
|
void setName(std::string const &name)
|
|
{
|
|
m_name = name;
|
|
}
|
|
/* User comment. */
|
|
std::string const &comment() const
|
|
{
|
|
return m_comment;
|
|
}
|
|
void setComment(std::string const &comment)
|
|
{
|
|
m_comment = comment;
|
|
}
|
|
|
|
/* Check for a non-empty intersection between two objects. */
|
|
bool intersects(BinaryObject const &other) const;
|
|
|
|
/* Check whether this object contains another object. */
|
|
bool contains(BinaryObject const &other) const;
|
|
|
|
/* User-readable string representation: "object <name> at <addr>" */
|
|
std::string str() const;
|
|
|
|
// TODO: BinaryObject serialization
|
|
|
|
/* Polymorphic accessors */
|
|
bool isMark() const
|
|
{
|
|
return m_type == Type::Mark;
|
|
}
|
|
bool isVariable() const
|
|
{
|
|
return m_type == Type::Variable;
|
|
}
|
|
bool isFunction() const
|
|
{
|
|
return m_type == Type::Function;
|
|
}
|
|
FxOS::Mark &getMark() const
|
|
{
|
|
assert(isMark() && "wrong BinaryObject accessor: not a Mark");
|
|
return *(FxOS::Mark *)this;
|
|
}
|
|
FxOS::Variable &getVariable() const
|
|
{
|
|
assert(isVariable() && "wrong BinaryObject accessor: not a Variable");
|
|
return *(FxOS::Variable *)this;
|
|
}
|
|
FxOS::Function &getFunction() const
|
|
{
|
|
assert(isFunction() && "wrong BinaryObject accessor: not a Function");
|
|
return *(FxOS::Function *)this;
|
|
}
|
|
|
|
private:
|
|
Binary &m_binary;
|
|
u32 m_address;
|
|
u32 m_size;
|
|
Type const m_type;
|
|
|
|
std::string m_name;
|
|
/* TODO: BinaryObject: don't reserve 32 bytes for empty comments */
|
|
std::string m_comment;
|
|
};
|
|
|
|
struct Binary
|
|
{
|
|
/* Empty binary with an empty virtual space. */
|
|
Binary() = default;
|
|
BSON serialize() const;
|
|
void deserialize(BSON const &);
|
|
|
|
VirtualSpace &vspace()
|
|
{
|
|
return m_vspace;
|
|
}
|
|
VirtualSpace const &vspace() const
|
|
{
|
|
return m_vspace;
|
|
}
|
|
|
|
/* OS analysis (performed on-demand). Returns the new or cached OS
|
|
analysis results, nullptr if analysis failed. */
|
|
OS *OSAnalysis(bool force = false) const;
|
|
|
|
// TODO: Platform information in a binary
|
|
// TODO: Implement OS analysis
|
|
// TODO: Add and manage objects
|
|
|
|
std::multimap<u32, std::unique_ptr<BinaryObject>> const &objects() const
|
|
{
|
|
return m_objects;
|
|
}
|
|
|
|
auto functions() // -> [Function &]
|
|
{
|
|
return std::views::all(m_objects) | std::views::filter([](auto &pair) {
|
|
return pair.second->isFunction();
|
|
}) | std::views::transform([](auto &pair) -> Function & {
|
|
return pair.second->getFunction();
|
|
});
|
|
}
|
|
auto functions() const // -> [Function const &]
|
|
{
|
|
return std::views::all(m_objects) | std::views::filter([](auto &pair) {
|
|
return pair.second->isFunction();
|
|
}) | std::views::transform([](auto &pair) -> Function const & {
|
|
return pair.second->getFunction();
|
|
});
|
|
}
|
|
|
|
/* Iterator on only variables; yields a series of Variable [const] &. */
|
|
auto variables()
|
|
{
|
|
return std::views::all(m_objects) | std::views::filter([](auto &pair) {
|
|
return pair.second->isVariable();
|
|
}) | std::views::transform([](auto &pair) -> Variable & {
|
|
return pair.second->getVariable();
|
|
});
|
|
}
|
|
auto variables() const
|
|
{
|
|
return std::views::all(m_objects) | std::views::filter([](auto &pair) {
|
|
return pair.second->isVariable();
|
|
}) | std::views::transform([](auto &pair) -> Variable const & {
|
|
return pair.second->getVariable();
|
|
});
|
|
}
|
|
|
|
/* Add an object to the binary. */
|
|
void addObject(std::unique_ptr<BinaryObject> &&obj);
|
|
|
|
/* Return the address of an object by name, if it exists. If there are
|
|
multiple objects with the same name, returns an arbitrary one. */
|
|
std::optional<u32> objectAddress(std::string const &name) const;
|
|
|
|
/* Return the address of an object defined at the specified address if
|
|
there is one, nullptr otherwise. If multiple objects are defined at the
|
|
specified address, an arbitrary one is returned. */
|
|
BinaryObject *objectAt(u32 address);
|
|
BinaryObject const *objectAt(u32 address) const;
|
|
|
|
/* Returns the list of all objects defined at the specified address. */
|
|
std::vector<BinaryObject *> objectsAt(u32 address);
|
|
std::vector<BinaryObject const *> objectsAt(u32 address) const;
|
|
|
|
/* Locate all objects that intersect an address. */
|
|
std::vector<BinaryObject *> objectsCovering(u32 address);
|
|
std::vector<BinaryObject const *> objectsCovering(u32 address) const;
|
|
|
|
/* Return one or all functions defined at a given address. */
|
|
Function *functionAt(u32 address);
|
|
Function const *functionAt(u32 address) const;
|
|
std::vector<Function *> functionsAt(u32 address);
|
|
std::vector<Function const *> functionsAt(u32 address) const;
|
|
|
|
private:
|
|
VirtualSpace m_vspace;
|
|
|
|
/* OS analysis results */
|
|
mutable std::unique_ptr<OS> m_os;
|
|
|
|
/* All binary objects */
|
|
std::multimap<u32, std::unique_ptr<BinaryObject>> m_objects;
|
|
};
|
|
|
|
/* Basic, unattributed binary object used to mark out regions of code (eg.
|
|
"kernel", "timer driver", "LINK app", "zero", "interrupt handlers"...). May
|
|
alias with other binary objects. */
|
|
struct Mark: public BinaryObject
|
|
{
|
|
// TODO: Tags/colors in marks
|
|
|
|
// TODO: BinaryObject serialization
|
|
};
|
|
|
|
/* Binary object representing a non-empty piece of data. */
|
|
struct Variable: public BinaryObject
|
|
{
|
|
/* Create a new variable at the given location with the given name. The
|
|
type defaults to an unsigned machine word (u32). */
|
|
Variable(Binary &binary, u32 address, std::string const &name);
|
|
|
|
#if 0
|
|
DataType const *type() const
|
|
{
|
|
return m_type;
|
|
}
|
|
void setType(DataType const &type)
|
|
{
|
|
m_type = type;
|
|
BinaryObject::setSize(m_type.size());
|
|
}
|
|
|
|
private:
|
|
DataType const *m_type;
|
|
#endif
|
|
};
|
|
|
|
} /* namespace FxOS */
|
|
|
|
#endif /* FXOS_BINARY_H */
|