Skip to content

Commit

Permalink
qt/bfd_binary_parser: Allow selecting architecture if it's not detected
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonKagstrom committed Jul 7, 2024
1 parent cde1e34 commit a153c62
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 30 deletions.
5 changes: 3 additions & 2 deletions include/emilpro/i_binary_parser.hh
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ public:
virtual ~IBinaryParser() = default;

virtual void
ForAllSections(std::function<void(std::unique_ptr<emilpro::ISection>)> on_section) = 0;
ForAllSections(const std::function<void(std::unique_ptr<emilpro::ISection>)> &on_section) = 0;

virtual Machine GetMachine() const = 0;

static std::unique_ptr<IBinaryParser> FromFile(std::string_view path);
static std::unique_ptr<IBinaryParser> FromFile(std::string_view path,
std::optional<Machine> machine_hint = std::nullopt);
};

} // namespace emilpro
7 changes: 6 additions & 1 deletion include/emilpro/machine.hh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include <cstdint>
#include <optional>
#include <string_view>

namespace emilpro
{
Expand All @@ -18,4 +20,7 @@ enum class Machine : uint8_t
kUnknown,
};

}
const char* MachineToString(Machine machine);
std::optional<Machine> MachineFromString(std::string_view str);

} // namespace emilpro
2 changes: 1 addition & 1 deletion include/emilpro/mock/mock_binary_parser.hh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class MockBinaryParser : public IBinaryParser
{
public:
MAKE_CONST_MOCK0(GetMachine, Machine(), final);
MAKE_MOCK1(ForAllSections, void(std::function<void(std::unique_ptr<ISection>)>), final);
MAKE_MOCK1(ForAllSections, void(const std::function<void(std::unique_ptr<ISection>)>&), final);
};

} // namespace emilpro::mock
2 changes: 1 addition & 1 deletion qt/emilpro/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ main(int argc, char* argv[])
{
if (auto err = w.LoadFile(argv[1]); err)
{
fmt::print("Error loading file: {}\n\n", err);
fmt::print("Error loading file: {}\n\n", MainWindow::LoadErrorToString(*err));
Usage(argv[0]);
}
}
Expand Down
97 changes: 88 additions & 9 deletions qt/emilpro/mainwindow.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <QFile>
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QMetaType>
#include <QScrollBar>
Expand All @@ -29,6 +30,29 @@ auto kSymbolDynamicColor = QBrush("lightgreen");

} // namespace

const char*
MainWindow::LoadErrorToString(LoadError error)
{
constexpr auto kErrorStrings = std::array {
std::pair {LoadError::kFileNotFound, "File not found"},
std::pair {LoadError::kParseError, "Parse error"},
std::pair {LoadError::kUnknownArchitecture, "Unknown architecture"},
};

if (auto it = std::find_if(kErrorStrings.begin(),
kErrorStrings.end(),
[error](auto& p) { return p.first == error; });
it != kErrorStrings.end())
{
return it->second;
}

// Programming error
assert(false);

return "";
}

MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, m_ui(new Ui::MainWindow)
Expand Down Expand Up @@ -72,18 +96,23 @@ MainWindow::~MainWindow()
delete m_ui;
}

const char*
MainWindow::LoadFile(const std::string& filename)
std::optional<MainWindow::LoadError>
MainWindow::LoadFile(const std::string& filename, std::optional<emilpro::Machine> machine_hint)
{
auto parser = emilpro::IBinaryParser::FromFile(filename);
auto parser = emilpro::IBinaryParser::FromFile(filename, machine_hint);
if (!parser)
{
return "parse error";
return LoadError::kParseError;
}
if (parser->GetMachine() == emilpro::Machine::kUnknown)
{
return LoadError::kUnknownArchitecture;
}

auto disassembler = emilpro::IDisassembler::CreateFromArchitecture(parser->GetMachine());
if (!disassembler)
{
return "unsupported architecture";
return LoadError::kUnknownArchitecture;
}


Expand Down Expand Up @@ -173,7 +202,7 @@ MainWindow::LoadFile(const std::string& filename)
}
m_visible_symbols = m_database.Symbols();

return nullptr;
return std::nullopt;
}

void
Expand Down Expand Up @@ -284,10 +313,37 @@ MainWindow::on_action_Open_triggered(bool activated)
{
auto filename = QFileDialog::getOpenFileName(this, tr("Open binary"));

if (auto err = LoadFile(filename.toStdString()); err)
if (filename.isEmpty())
{
// Cancel
return;
}

auto err = LoadFile(filename.toStdString());
if (err)
{
if (err == LoadError::kUnknownArchitecture)
{
auto machine = SelectArchitecture();

if (machine)
{
err = LoadFile(filename.toStdString(), machine);
}
else
{
// Cancel, do nothing
return;
}
}
}

// Still not OK?
if (err)
{
QMessageBox::critical(
this, "?LOAD ERROR", fmt::format("Cannot load file: {}", err).c_str());
QMessageBox::critical(this,
"?LOAD ERROR",
fmt::format("Cannot load file: {}", LoadErrorToString(*err)).c_str());
}
}

Expand Down Expand Up @@ -1043,6 +1099,29 @@ MainWindow::SetRowColor(QAbstractItemModel* model,
}
}

std::optional<emilpro::Machine>
MainWindow::SelectArchitecture()
{
QStringList architectures;

for (auto i = 0; i < static_cast<int>(emilpro::Machine::kUnknown); i++)
{
architectures << QString::fromStdString(MachineToString(static_cast<emilpro::Machine>(i)));
}

auto ok = false;
auto selected = QInputDialog::getItem(
this, "Select Architecture", "Architecture:", architectures, 0, false, &ok);

std::optional<emilpro::Machine> machine;
if (ok && !selected.isEmpty())
{
machine = emilpro::MachineFromString(selected.toStdString());
}

return machine;
}

bool
MainWindow::eventFilter(QObject* watched, QEvent* event)
{
Expand Down
21 changes: 17 additions & 4 deletions qt/emilpro/mainwindow.hh
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,22 @@ class MainWindow : public QMainWindow
Q_OBJECT

public:
explicit MainWindow(QWidget* parent = 0);
~MainWindow();
enum class LoadError
{
kFileNotFound,
kParseError,
kUnknownArchitecture,
kValueCount,
};

/// Parse a file, and return nullptr if successful, otherwise an error string
const char* LoadFile(const std::string& filename);
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow() final;

/// Parse a file, and return nullptr if successful, otherwise an error code
std::optional<LoadError> LoadFile(const std::string& filename,
std::optional<emilpro::Machine> machine_hint = std::nullopt);

static const char* LoadErrorToString(LoadError error);

// On quit etc
void UpdatePreferences();
Expand Down Expand Up @@ -112,6 +123,8 @@ private:

void UpdateHistoryView();

std::optional<emilpro::Machine> SelectArchitecture();

const QString& LookupSourceFile(std::string_view);

// From https://forum.qt.io/topic/76265/set-background-of-specific-row-in-qtableview/2
Expand Down
18 changes: 12 additions & 6 deletions src/binary_parser/bfd_binary_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ mem_bfd_iovec_stat(struct bfd* abfd, void* stream, struct stat* sb)
}


BfdBinaryParser::BfdBinaryParser(std::string_view path)
BfdBinaryParser::BfdBinaryParser(std::string_view path, std::optional<Machine> machine_hint)
: m_path(path)
, m_machine_hint(machine_hint)
{
}

Expand All @@ -109,7 +110,7 @@ BfdBinaryParser::GetMachine() const
}

void
BfdBinaryParser::ForAllSections(std::function<void(std::unique_ptr<ISection>)> on_section)
BfdBinaryParser::ForAllSections(const std::function<void(std::unique_ptr<ISection>)>& on_section)
{
for (auto& [bfd_section, sec] : m_pending_sections)
{
Expand Down Expand Up @@ -316,10 +317,15 @@ BfdBinaryParser::Parse()
if (it_machine != kMachineMap.end())
{
m_machine = it_machine->second;
if (m_machine == Machine::kArm && m_arm_in_thumb_mode)
{
m_machine = Machine::kArmThumb;
}
}
else if (m_machine_hint)
{
m_machine = *m_machine_hint;
}

if (m_machine == Machine::kArm && m_arm_in_thumb_mode)
{
m_machine = Machine::kArmThumb;
}

return true;
Expand Down
7 changes: 4 additions & 3 deletions src/binary_parser/bfd_binary_parser.hh
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ namespace emilpro
class BfdBinaryParser : public IBinaryParser
{
public:
explicit BfdBinaryParser(std::string_view path);
explicit BfdBinaryParser(std::string_view path, std::optional<Machine> machine_hint);

virtual ~BfdBinaryParser() final;
~BfdBinaryParser() final;

bool Parse();

private:
Machine GetMachine() const final;
void ForAllSections(std::function<void(std::unique_ptr<ISection>)> on_section) final;
void ForAllSections(const std::function<void(std::unique_ptr<ISection>)>& on_section) final;

std::optional<Section::FileLine>
LookupLine(bfd_section* section, bfd_symbol** symTbl, uint64_t offset);
Expand All @@ -40,6 +40,7 @@ private:

Machine m_machine {Machine::kUnknown};
std::string_view m_path;
const std::optional<Machine> m_machine_hint;

uint8_t* m_rawData {nullptr};
size_t m_rawDataSize {0};
Expand Down
49 changes: 46 additions & 3 deletions src/binary_parser/binary_parser_factory.cc
Original file line number Diff line number Diff line change
@@ -1,12 +1,55 @@
#include "bfd_binary_parser.hh"
#include "emilpro/i_binary_parser.hh"
#include "emilpro/machine.hh"

#include "bfd_binary_parser.hh"
#include <array>
#include <utility>

using namespace emilpro;

std::unique_ptr<IBinaryParser> IBinaryParser::FromFile(std::string_view path)
constexpr auto kMachineTable = std::array {
std::pair {Machine::kX86, "Intel x86"},
std::pair {Machine::kAmd64, "AMD64"},
std::pair {Machine::kArm, "ARM"},
std::pair {Machine::kArmThumb, "ARM (thumb mode)"},
std::pair {Machine::kArm64, "ARM64"},
std::pair {Machine::kMips, "MIPS"},
std::pair {Machine::kPpc, "PowerPC"},
};
static_assert(kMachineTable.size() == static_cast<size_t>(Machine::kUnknown));

// Put them here for now, although not a good place
const char*
emilpro::MachineToString(Machine machine)
{
if (auto it = std::ranges::find_if(
kMachineTable, [machine](const auto& pair) { return pair.first == machine; });
it != kMachineTable.end())
{
return it->second;
}

return "";
}

std::optional<Machine>
emilpro::MachineFromString(std::string_view str)
{
if (auto it = std::ranges::find_if(kMachineTable,
[str](const auto& pair) { return pair.second == str; });
it != kMachineTable.end())
{
return it->first;
}

return std::nullopt;
}


std::unique_ptr<IBinaryParser>
IBinaryParser::FromFile(std::string_view path, std::optional<Machine> machine_hint)
{
auto out = std::make_unique<BfdBinaryParser>(path);
auto out = std::make_unique<BfdBinaryParser>(path, machine_hint);

if (!out->Parse())
{
Expand Down

0 comments on commit a153c62

Please sign in to comment.