fxos: save binary objects (currently functions) in project

This commit is contained in:
Lephenixnoir 2024-01-08 21:10:12 +01:00
parent f5ad03152d
commit 80d6001417
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
7 changed files with 143 additions and 22 deletions

View File

@ -142,7 +142,7 @@ struct Binary
/* Empty binary with an empty virtual space. */
Binary() = default;
BSON serialize() const;
void deserialize(BSON const &);
void deserialize(BSON const &, bool verbose = false);
VirtualSpace &vspace()
{
@ -243,9 +243,15 @@ private:
alias with other binary objects. */
struct Mark: public BinaryObject
{
Mark(Binary &binary, u32 address, u32 size):
BinaryObject(binary, Type::Mark, address, size)
{
}
// TODO: Tags/colors in marks
// TODO: BinaryObject serialization
BSON serialize() const;
void deserialize(BSON const &);
};
/* Binary object representing a non-empty piece of data. */
@ -255,6 +261,9 @@ struct Variable: public BinaryObject
type defaults to an unsigned machine word (u32). */
Variable(Binary &binary, u32 address, std::string const &name);
BSON serialize() const;
void deserialize(BSON const &);
#if 0
DataType const *type() const
{

View File

@ -34,6 +34,9 @@ struct Function: public BinaryObject
{
Function(Binary &binary, u32 address);
BSON serialize() const;
void deserialize(BSON const &);
/* Number of basic blocks. */
uint blockCount() const
{

View File

@ -51,7 +51,7 @@ struct Project
/* Save; prints on stderr and returns false in case of error. */
bool save();
/* Load from a folder. */
bool load(std::string const &path);
bool load(std::string const &path, bool verbose = false);
/* Create a binary based on the provided name. If there is a conflict, the
name will get a suffix like "_0", "_1", etc. Final name is returned. */

View File

@ -6,6 +6,8 @@
//---------------------------------------------------------------------------//
#include <fxos/binary.h>
#include <fxos/function.h>
#include <fxos/analysis.h>
using namespace FxOS;
//=== Binary ===//
@ -17,9 +19,14 @@ BSON Binary::serialize() const
for(auto const &[address, obj]: m_objects) {
char str[32];
sprintf(str, "%08x", address);
// TODO: Serialize BinaryObjects
// new(&fields[i]) BSONField(str, obj->serialize());
new(&fields[i]) BSONField(str, BSON::mkNull());
if(obj->isMark())
new(&fields[i]) BSONField(str, obj->getMark().serialize());
else if(obj->isVariable())
new(&fields[i]) BSONField(str, obj->getVariable().serialize());
else if(obj->isFunction())
new(&fields[i]) BSONField(str, obj->getFunction().serialize());
i++;
}
@ -30,7 +37,7 @@ BSON Binary::serialize() const
});
}
void Binary::deserialize(BSON const &b)
void Binary::deserialize(BSON const &b, bool verbose)
{
assert(b.isDocument() && b["*"].getString() == "Binary");
m_vspace.deserialize(b["vspace"]);
@ -39,9 +46,38 @@ void Binary::deserialize(BSON const &b)
int N = b["objects"].size();
for(int i = 0; i < N; i++) {
if(verbose) {
printf("[%04d/%04d] Loading objects...", i, N);
fflush(stdout);
printf("\r\e[K");
}
// TODO: This is redundant, the object stores it better already
uint32_t address = std::stoul(fields[i].getName(), nullptr, 16);
// TODO: Deserialize BinaryObject from fields[i].value()
BSON const &obj = fields[i].value();
assert(address == (u32)obj["address"].getI32());
/* Because we have to create a different class for each object type, we
skip BinaryObject and go to derived classes immediately. */
std::string type = obj["*"].getString();
if(type == "Mark") {
auto ptr = std::make_unique<Mark>(*this, address, 0);
ptr->deserialize(obj);
m_objects.emplace(address, std::move(ptr));
}
else if(type == "Variable") {
auto ptr = std::make_unique<Variable>(*this, address, "");
ptr->deserialize(obj);
m_objects.emplace(address, std::move(ptr));
}
else if(type == "Function") {
auto ptr = std::make_unique<Function>(*this, address);
ptr->deserialize(obj);
m_objects.emplace(address, std::move(ptr));
}
}
fflush(stdout);
}
OS *Binary::OSAnalysis(bool force) const
@ -178,6 +214,29 @@ bool BinaryObject::contains(BinaryObject const &other) const
&& m_address + m_size >= other.address() + other.size();
}
//=== Mark ===//
BSON Mark::serialize() const
{
// TODO: Serialize Mark is a non-trivial way
return BSON::mkDocument({
{"*", BSON::mkString("Mark")},
{"address", BSON::mkI32(address())},
{"size", BSON::mkI32(size())},
{"name", BSON::mkString(name())},
{"comm", comment() != "" ? BSON::mkString(comment()) : BSON::mkNull()},
});
}
void Mark::deserialize(BSON const &b)
{
// TODO: Serialize Mark is a non-trivial way
assert(b.isDocument() && b["*"].getString() == "Mark");
setSize(b["size"].getI32());
setName(b["name"].getString());
setComment(b["comm"].isNull() ? "" : b["comment"].getString());
}
//=== Variable ===//
Variable::Variable(Binary &binary, u32 address, std::string const &name):
@ -186,3 +245,24 @@ Variable::Variable(Binary &binary, u32 address, std::string const &name):
setName(name);
// m_type = IntegerType(4, false);
}
BSON Variable::serialize() const
{
// TODO: Serialize Variable is a non-trivial way
return BSON::mkDocument({
{"*", BSON::mkString("Variable")},
{"address", BSON::mkI32(address())},
{"size", BSON::mkI32(size())},
{"name", BSON::mkString(name())},
{"comm", comment() != "" ? BSON::mkString(comment()) : BSON::mkNull()},
});
}
void Variable::deserialize(BSON const &b)
{
// TODO: Serialize Variable is a non-trivial way
assert(b.isDocument() && b["*"].getString() == "Variable");
setSize(b["size"].getI32());
setName(b["name"].getString());
setComment(b["comm"].isNull() ? "" : b["comment"].getString());
}

View File

@ -27,6 +27,28 @@ Function::Function(Binary &binary, u32 address):
setName(format("fun.%08x", address));
}
BSON Function::serialize() const
{
// TODO: Function: Serialize certain analysis results?
return BSON::mkDocument({
{"*", BSON::mkString("Function")},
{"address", BSON::mkI32(address())},
{"name", BSON::mkString(name())},
{"comm", comment() != "" ? BSON::mkString(comment()) : BSON::mkNull()},
});
}
void Function::deserialize(BSON const &b)
{
assert(b.isDocument() && b["*"].getString() == "Function");
exploreFunctionAt(address());
setName(b["name"].getString());
setComment(b["comm"].isNull() ? "" : b["comment"].getString());
runAnalysis();
}
BasicBlock &Function::basicBlockByAddress(u32 pc)
{
for(BasicBlock &bb: *this) {

View File

@ -1,5 +1,6 @@
#include <fxos/project.h>
#include <fxos/util/log.h>
#include <fxos/util/Timer.h>
#include <filesystem>
#include <algorithm>
#include <string>
@ -164,10 +165,13 @@ bool Project::save()
return true;
}
bool Project::load(std::string const &path0)
bool Project::load(std::string const &path0, bool verbose)
{
fs::path path = path0;
Timer t;
t.start();
BSON metadata
= BSON::loadDocumentFromFile(path / "project", true, true, "Project");
if(metadata.isNull())
@ -177,21 +181,24 @@ bool Project::load(std::string const &path0)
m_name = metadata["name"].getString();
m_dirty = false;
if(!metadata.hasField("binaries"))
return true;
if(metadata.hasField("binaries")) {
for(size_t i = 0; i < metadata["binaries"].size(); i++) {
std::string name = metadata["binaries"][i].getString();
BSON bin = BSON::loadDocumentFromFile(
path / (name + ".bin"), true, true, "Binary");
if(bin.isNull())
continue;
for(size_t i = 0; i < metadata["binaries"].size(); i++) {
std::string name = metadata["binaries"][i].getString();
BSON bin = BSON::loadDocumentFromFile(
path / (name + ".bin"), true, true, "Binary");
if(bin.isNull())
continue;
assert(m_binaries.count(name) == 0);
m_binaries[name];
m_binaries[name].deserialize(bin);
assert(m_binaries.count(name) == 0);
m_binaries[name];
m_binaries[name].deserialize(bin, verbose);
}
}
t.stop();
if(verbose)
printf("Loaded %s in %s\n", m_name.c_str(), t.format_time().c_str());
return true;
}

View File

@ -105,7 +105,7 @@ void Session::switchToNewProject(
bool Session::loadProject(std::string const &path)
{
auto p = std::make_unique<Project>();
if(!p->load(path))
if(!p->load(path, true))
return false;
/* Unload current project - any unsaved data is lost. */