summaryrefslogtreecommitdiff
path: root/view/sharedcache/ui
diff options
context:
space:
mode:
authorAlexander Taylor <alex@vector35.com>2025-04-09 12:47:00 -0400
committerAlexander Taylor <alex@vector35.com>2025-04-09 17:16:23 -0400
commite3772366504d6d05b4809013cb4f4149ca64c4d4 (patch)
treee3fcb0818bfede1a5f8fff0b1edd59422b517b67 /view/sharedcache/ui
parentf78345462ebc25e0241d838f04104e5c6bfae863 (diff)
Shared cache and kernel cache UI cleanup.
One day, these will share more code, but today is not quite that day.
Diffstat (limited to 'view/sharedcache/ui')
-rw-r--r--view/sharedcache/ui/dsctriage.cpp621
-rw-r--r--view/sharedcache/ui/dsctriage.h128
-rw-r--r--view/sharedcache/ui/symboltable.cpp5
3 files changed, 390 insertions, 364 deletions
diff --git a/view/sharedcache/ui/dsctriage.cpp b/view/sharedcache/ui/dsctriage.cpp
index 09b075f0..6b0ec0d5 100644
--- a/view/sharedcache/ui/dsctriage.cpp
+++ b/view/sharedcache/ui/dsctriage.cpp
@@ -1,23 +1,21 @@
-//
-// Created by kat on 8/15/24.
-//
-
-#include "ui/fontsettings.h"
-#include "tabwidget.h"
+#include "dsctriage.h"
#include "globalarea.h"
#include "progresstask.h"
-#include <QMessageBox>
-#include <QHeaderView>
-#include "dsctriage.h"
#include "symboltable.h"
+#include "ui/fontsettings.h"
+#include <QHeaderView>
+#include <QMessageBox>
+#include <utility>
using namespace BinaryNinja;
using namespace SharedCacheAPI;
+
DSCTriageViewType::DSCTriageViewType()
: ViewType("DSCTriage", "Dyld Shared Cache Triage")
{}
+
int DSCTriageViewType::getPriority(BinaryViewRef data, const QString& filename)
{
if (data->GetTypeName() == VIEW_NAME)
@@ -25,6 +23,7 @@ int DSCTriageViewType::getPriority(BinaryViewRef data, const QString& filename)
return 0;
}
+
QWidget* DSCTriageViewType::create(BinaryViewRef data, ViewFrame* viewFrame)
{
if (data->GetTypeName() != VIEW_NAME)
@@ -33,12 +32,14 @@ QWidget* DSCTriageViewType::create(BinaryViewRef data, ViewFrame* viewFrame)
return new DSCTriageView(viewFrame, data);
}
+
void DSCTriageViewType::Register()
{
registerViewType(new DSCTriageViewType());
}
-DSCTriageView::DSCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(parent), m_data(data)
+
+DSCTriageView::DSCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(parent), m_data(std::move(data))
{
setBinaryDataNavigable(false);
setupView(this);
@@ -51,408 +52,423 @@ DSCTriageView::DSCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(pare
auto triageTabStyle = new GlobalAreaTabStyle();
m_triageTabs->setTabStyle(triageTabStyle);
- QWidget* defaultWidget = nullptr;
+ QWidget* defaultWidget = initImageTable();
+ initSymbolTable();
+ initCacheInfoTables();
- auto loadImagesWithAddr = [this](const std::vector<uint64_t>& addresses, bool includeDependencies = false) {
- auto controller = SharedCacheController::GetController(*this->m_data);
- if (!controller)
- return;
+ m_layout = new QVBoxLayout(this);
+ m_layout->addWidget(m_triageTabs);
+ setLayout(m_layout);
+
+ // In case we have already initialized the controller (user has opened this view type again)
+ // we will call refresh data. If this is the first triage view constructed (i.e. before view init) then this
+ // will do nothing.
+ RefreshData();
+
+ m_triageTabs->selectWidget(defaultWidget);
+}
+
+
+DSCTriageView::~DSCTriageView()
+{
+ UIContext::unregisterNotification(this);
+}
+
+
+void DSCTriageView::loadImagesWithAddr(const std::vector<uint64_t>& addresses, bool includeDependencies) {
+ auto controller = SharedCacheController::GetController(*this->m_data);
+ if (!controller)
+ return;
- std::map<uint64_t, CacheImage> images = {};
- for (const uint64_t& addr : addresses)
+ std::map<uint64_t, CacheImage> images = {};
+ for (const uint64_t& addr : addresses)
+ {
+ auto image = controller->GetImageContaining(addr);
+ // Only try to load if we have not already.
+ if (image.has_value() && !controller->IsImageLoaded(*image))
{
- auto image = controller->GetImageContaining(addr);
- // Only try to load if we have not already.
- if (image.has_value() && !controller->IsImageLoaded(*image))
- {
- images.insert({image->headerAddress, *image});
+ images.insert({image->headerAddress, *image});
- // TODO: We currently only add direct dependencies, may want to make the depth configurable?
- if (includeDependencies)
+ // TODO: We currently only add direct dependencies, may want to make the depth configurable?
+ if (includeDependencies)
+ {
+ auto dependencies = controller->GetImageDependencies(*image);
+ for (const auto& depName : dependencies)
{
- auto dependencies = controller->GetImageDependencies(*image);
- for (const auto& depName : dependencies)
+ auto depImage = controller->GetImageWithName(depName);
+ if (depImage.has_value() && !controller->IsImageLoaded(*depImage))
{
- auto depImage = controller->GetImageWithName(depName);
- if (depImage.has_value() && !controller->IsImageLoaded(*depImage))
- {
- images.insert({depImage->headerAddress, *depImage});
- }
+ images.insert({depImage->headerAddress, *depImage});
}
}
}
-
}
+ }
- // Don't create a worker action if we don't have any images.
- if (images.empty())
- return;
+ // Don't create a worker action if we don't have any images.
+ if (images.empty())
+ return;
- WorkerPriorityEnqueue([controller, this, images]() {
- size_t loadedImages = 0;
- const std::string initialLoad = fmt::format("Loading images... (0/{})", images.size());
- auto imageLoadTask = BackgroundTask(initialLoad, true);
+ WorkerPriorityEnqueue([controller, this, images]() {
+ size_t loadedImages = 0;
+ const std::string initialLoad = fmt::format("Loading images... (0/{})", images.size());
+ auto imageLoadTask = BackgroundTask(initialLoad, true);
- for (const auto& [addr, image] : images)
- {
- if (imageLoadTask.IsCancelled())
- break;
- std::string newLoad = fmt::format("Loading images... ({}/{})", loadedImages++, images.size());
- imageLoadTask.SetProgressText(newLoad);
- if (controller->ApplyImage(*this->m_data, image))
- setImageLoaded(image.headerAddress);
- }
- imageLoadTask.Finish();
+ for (const auto& [addr, image] : images)
+ {
+ if (imageLoadTask.IsCancelled())
+ break;
+ std::string newLoad = fmt::format("Loading images... ({}/{})", loadedImages++, images.size());
+ imageLoadTask.SetProgressText(newLoad);
+ if (controller->ApplyImage(*this->m_data, image))
+ setImageLoaded(image.headerAddress);
+ }
+ imageLoadTask.Finish();
+
+ // We have loaded images, lets make sure to update analysis!
+ this->m_data->AddAnalysisOption("linearsweep");
+ this->m_data->UpdateAnalysis();
+ });
+}
- // We have loaded images, lets make sure to update analysis!
- this->m_data->AddAnalysisOption("linearsweep");
- this->m_data->UpdateAnalysis();
- });
- };
- // Tab: Images
+QWidget* DSCTriageView::initImageTable()
+{
auto loadImageTable = new FilterableTableView(this);
- {
- m_imageModel = new QStandardItemModel(0, 3, loadImageTable);
- m_imageModel->setHorizontalHeaderLabels({"Address", "Loaded", "Name"});
- // Apply custom column styling
- loadImageTable->setItemDelegateForColumn(0, new AddressColorDelegate(loadImageTable));
- loadImageTable->setItemDelegateForColumn(1, new LoadedDelegate(loadImageTable));
+ m_imageModel = new QStandardItemModel(0, 3, loadImageTable);
+ m_imageModel->setHorizontalHeaderLabels({"Address", "Loaded", "Name"});
- // Context menu
- loadImageTable->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(loadImageTable, &QWidget::customContextMenuRequested, [loadImageTable, loadImagesWithAddr](const QPoint &pos) {
- QMenu contextMenu(tr("Load Image Actions"), loadImageTable);
+ // Apply custom column styling
+ loadImageTable->setItemDelegateForColumn(0, new AddressColorDelegate(loadImageTable));
+ loadImageTable->setItemDelegateForColumn(1, new LoadedDelegate(loadImageTable));
- // Get number of selected images
- auto selected = loadImageTable->selectionModel()->selectedRows();
- int selectedCount = 0;
- std::vector<uint64_t> addresses;
- for (const auto& idx : selected)
- {
- // Skip rows hidden by the filter
- if (loadImageTable->isRowHidden(idx.row()))
- continue;
- addresses.push_back(idx.data().toString().toULongLong(nullptr, 16));
- selectedCount++;
- }
+ // Context menu
+ loadImageTable->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(loadImageTable, &QWidget::customContextMenuRequested, [this, loadImageTable](const QPoint &pos) {
+ QMenu contextMenu(tr("Load Image Actions"), loadImageTable);
- QAction noSelectionAction("No Images Selected", loadImageTable);
- QAction loadImagesAction("", loadImageTable);
- QAction loadImagesWithDepsAction("", loadImageTable);
- if (selectedCount == 0)
- {
- noSelectionAction.setEnabled(false);
- contextMenu.addAction(&noSelectionAction);
- }
- else
- {
- // Format action text for loading selected images
- QString loadActionText = (selectedCount == 1) ? "Load Selected Image" : QString("Load %1 Selected Images").arg(selectedCount);
- loadImagesAction.setText(loadActionText);
- connect(&loadImagesAction, &QAction::triggered, [loadImagesWithAddr, addresses]() {
- loadImagesWithAddr(addresses, false);
- });
- contextMenu.addAction(&loadImagesAction);
-
- // Format action text for loading selected images with dependencies
- QString loadWithDepsActionText = (selectedCount == 1) ? "Load Selected Image and Dependencies" : QString("Load %1 Selected Images and Dependencies").arg(selectedCount);
- loadImagesWithDepsAction.setText(loadWithDepsActionText);
- connect(&loadImagesWithDepsAction, &QAction::triggered, [loadImagesWithAddr, addresses]() {
- loadImagesWithAddr(addresses, true);
- });
- contextMenu.addAction(&loadImagesWithDepsAction);
- }
+ // Get number of selected images
+ auto selected = loadImageTable->selectionModel()->selectedRows();
+ int selectedCount = 0;
+ std::vector<uint64_t> addresses;
+ for (const auto& idx : selected)
+ {
+ // Skip rows hidden by the filter
+ if (loadImageTable->isRowHidden(idx.row()))
+ continue;
+ addresses.push_back(idx.data().toString().toULongLong(nullptr, 16));
+ selectedCount++;
+ }
- contextMenu.exec(loadImageTable->viewport()->mapToGlobal(pos));
- });
+ QAction noSelectionAction("No Images Selected", loadImageTable);
+ QAction loadImagesAction("", loadImageTable);
+ QAction loadImagesWithDepsAction("", loadImageTable);
+ if (selectedCount == 0)
+ {
+ noSelectionAction.setEnabled(false);
+ contextMenu.addAction(&noSelectionAction);
+ }
+ else
+ {
+ // Format action text for loading selected images
+ QString loadActionText = (selectedCount == 1) ? "Load Selected Image" : QString("Load %1 Selected Images").arg(selectedCount);
+ loadImagesAction.setText(loadActionText);
+ connect(&loadImagesAction, &QAction::triggered, [this, addresses]() {
+ loadImagesWithAddr(addresses, false);
+ });
+ contextMenu.addAction(&loadImagesAction);
- auto loadImageButton = new QPushButton();
- connect(loadImageButton, &QPushButton::clicked,
- [loadImageTable, loadImagesWithAddr](bool) {
- auto selected = loadImageTable->selectionModel()->selectedRows();
- std::vector<uint64_t> addresses;
- for (const auto& idx : selected)
- {
- // Skip rows hidden by the filter
- if (loadImageTable->isRowHidden(idx.row()))
- continue;
- addresses.push_back(idx.data().toString().toULongLong(nullptr, 16));
- }
- loadImagesWithAddr(addresses);
+ // Format action text for loading selected images with dependencies
+ QString loadWithDepsActionText = (selectedCount == 1) ? "Load Selected Image and Dependencies" : QString("Load %1 Selected Images and Dependencies").arg(selectedCount);
+ loadImagesWithDepsAction.setText(loadWithDepsActionText);
+ connect(&loadImagesWithDepsAction, &QAction::triggered, [this, addresses]() {
+ this->loadImagesWithAddr(addresses, true);
});
- loadImageButton->setText("Load Selected");
+ contextMenu.addAction(&loadImagesWithDepsAction);
+ }
+
+ contextMenu.exec(loadImageTable->viewport()->mapToGlobal(pos));
+ });
- auto refreshDataButton = new QPushButton();
+ auto loadImageButton = new QPushButton();
+ connect(loadImageButton, &QPushButton::clicked, [this, loadImageTable](bool) {
+ auto selected = loadImageTable->selectionModel()->selectedRows();
+ std::vector<uint64_t> addresses;
+ for (const auto& idx : selected)
{
- // TODO: Might want to introduce a cooldown for this button (if we even keep it)
- connect(refreshDataButton, &QPushButton::clicked, [this](bool) { RefreshData(); });
- refreshDataButton->setText("Refresh");
- } // refreshDataButton
+ // Skip rows hidden by the filter
+ if (loadImageTable->isRowHidden(idx.row()))
+ continue;
+ addresses.push_back(idx.data().toString().toULongLong(nullptr, 16));
+ }
+ loadImagesWithAddr(addresses);
+ });
+ loadImageButton->setText("Load Selected");
- auto loadImageFilterEdit = new FilterEdit(loadImageTable);
- connect(loadImageFilterEdit, &FilterEdit::textChanged, [loadImageTable](const QString& filter) {
- loadImageTable->setFilter(filter.toStdString());
- });
+ auto refreshDataButton = new QPushButton();
+ {
+ // TODO: Might want to introduce a cooldown for this button (if we even keep it)
+ connect(refreshDataButton, &QPushButton::clicked, [this](bool) { RefreshData(); });
+ refreshDataButton->setText("Refresh");
+ } // refreshDataButton
- connect(loadImageTable, &FilterableTableView::activated, this, [=](const QModelIndex& index) {
- auto addr = m_imageModel->item(index.row(), 0)->text().toULongLong(nullptr, 16);
- loadImagesWithAddr({addr});
- });
+ auto loadImageFilterEdit = new FilterEdit(loadImageTable);
+ connect(loadImageFilterEdit, &FilterEdit::textChanged, [loadImageTable](const QString& filter) {
+ loadImageTable->setFilter(filter.toStdString());
+ });
+
+ connect(loadImageTable, &FilterableTableView::activated, this, [=](const QModelIndex& index) {
+ auto addr = m_imageModel->item(index.row(), 0)->text().toULongLong(nullptr, 16);
+ loadImagesWithAddr({addr});
+ });
- auto loadImageLayout = new QVBoxLayout;
- loadImageLayout->addWidget(loadImageFilterEdit);
- loadImageLayout->addWidget(loadImageTable);
- auto buttonLayout = new QHBoxLayout;
- buttonLayout->addWidget(loadImageButton);
- buttonLayout->addWidget(refreshDataButton);
- buttonLayout->setAlignment(Qt::AlignLeft);
- loadImageLayout->addLayout(buttonLayout);
+ auto loadImageLayout = new QVBoxLayout;
+ loadImageLayout->addWidget(loadImageFilterEdit);
+ loadImageLayout->addWidget(loadImageTable);
- auto loadImageWidget = new QWidget;
- loadImageWidget->setLayout(loadImageLayout);
+ auto buttonLayout = new QHBoxLayout;
+ buttonLayout->addWidget(loadImageButton);
+ buttonLayout->addWidget(refreshDataButton);
+ buttonLayout->setAlignment(Qt::AlignLeft);
+ loadImageLayout->addLayout(buttonLayout);
- loadImageTable->setModel(m_imageModel);
+ auto loadImageWidget = new QWidget;
+ loadImageWidget->setLayout(loadImageLayout);
- loadImageTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ loadImageTable->setModel(m_imageModel);
- loadImageTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
- loadImageTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
- loadImageTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
+ loadImageTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
- loadImageTable->setSelectionBehavior(QAbstractItemView::SelectRows);
- loadImageTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ loadImageTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
+ loadImageTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+ loadImageTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
- loadImageTable->setSortingEnabled(true);
+ loadImageTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ loadImageTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
- loadImageTable->verticalHeader()->setVisible(false);
+ loadImageTable->setSortingEnabled(true);
- m_triageTabs->addTab(loadImageWidget, "Images");
- defaultWidget = loadImageWidget;
- m_triageTabs->setCanCloseTab(loadImageWidget, false);
- } // loadImageTable
+ loadImageTable->verticalHeader()->setVisible(false);
- // Tab: Symbols
+ m_triageTabs->addTab(loadImageWidget, "Images");
+ m_triageTabs->setCanCloseTab(loadImageWidget, false);
+
+ return loadImageWidget; // For use as the default widget
+}
+
+
+void DSCTriageView::initSymbolTable()
+{
m_symbolTable = new SymbolTableView(this);
- {
- auto symbolFilterEdit = new FilterEdit(m_symbolTable);
- connect(symbolFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) {
- m_symbolTable->setFilter(filter.toStdString());
- });
- // Apply custom column styling
- m_symbolTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_symbolTable));
+ auto symbolFilterEdit = new FilterEdit(m_symbolTable);
+ connect(symbolFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) {
+ m_symbolTable->setFilter(filter.toStdString());
+ });
- auto loadSymbolImageButton = new QPushButton();
- {
- connect(loadSymbolImageButton, &QPushButton::clicked,
- [this, loadImagesWithAddr](bool) {
+ // Apply custom column styling
+ m_symbolTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_symbolTable));
+
+ auto loadSymbolImageButton = new QPushButton();
+ {
+ connect(loadSymbolImageButton, &QPushButton::clicked,
+ [this](bool) {
auto selected = m_symbolTable->selectionModel()->selectedRows();
std::vector<uint64_t> addresses;
for (const auto& row : selected)
addresses.push_back(row.data().toString().toULongLong(nullptr, 16));
loadImagesWithAddr(addresses);
});
- loadSymbolImageButton->setText("Load Image");
- } // loadImageButton
+ loadSymbolImageButton->setText("Load Image");
+ } // loadImageButton
- // Shows the current selected rows image name.
- auto currentImageLabel = new QLabel(this); {
- currentImageLabel->setText("");
- currentImageLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- }
-
- // Update the label whenever the selection changes.
- connect(m_symbolTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
- [this, currentImageLabel](const QModelIndex &current, const QModelIndex &) {
- auto symbol = m_symbolTable->getSymbolAtRow(current.row());
- auto controller = SharedCacheController::GetController(*this->m_data);
- if (!controller)
- return;
- auto image = controller->GetImageContaining(symbol.address);
- if (image)
- currentImageLabel->setText("Image: " + QString::fromStdString(image->name));
- else
- currentImageLabel->setText("");
- });
+ // Shows the current selected rows image name.
+ auto currentImageLabel = new QLabel(this); {
+ currentImageLabel->setText("");
+ currentImageLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ }
- auto symbolFooterLayout = new QHBoxLayout;
- symbolFooterLayout->addWidget(loadSymbolImageButton);
- symbolFooterLayout->addWidget(currentImageLabel);
+ // Update the label whenever the selection changes.
+ connect(m_symbolTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
+ [this, currentImageLabel](const QModelIndex &current, const QModelIndex &) {
+ auto symbol = m_symbolTable->getSymbolAtRow(current.row());
+ auto controller = SharedCacheController::GetController(*this->m_data);
+ if (!controller)
+ return;
+ auto image = controller->GetImageContaining(symbol.address);
+ if (image)
+ currentImageLabel->setText("Image: " + QString::fromStdString(image->name));
+ else
+ currentImageLabel->setText("");
+ });
- symbolFooterLayout->setAlignment(Qt::AlignLeft);
+ auto symbolFooterLayout = new QHBoxLayout;
+ symbolFooterLayout->addWidget(loadSymbolImageButton);
+ symbolFooterLayout->addWidget(currentImageLabel);
- auto symbolLayout = new QVBoxLayout;
- symbolLayout->addWidget(symbolFilterEdit);
- symbolLayout->addWidget(m_symbolTable);
- symbolLayout->addLayout(symbolFooterLayout);
+ symbolFooterLayout->setAlignment(Qt::AlignLeft);
- auto symbolWidget = new QWidget;
- symbolWidget->setLayout(symbolLayout);
+ auto symbolLayout = new QVBoxLayout;
+ symbolLayout->addWidget(symbolFilterEdit);
+ symbolLayout->addWidget(m_symbolTable);
+ symbolLayout->addLayout(symbolFooterLayout);
- connect(m_symbolTable, &SymbolTableView::activated, this, [=](const QModelIndex& index){
- auto symbol = m_symbolTable->getSymbolAtRow(index.row());
- auto dialog = new QMessageBox(this);
+ auto symbolWidget = new QWidget;
+ symbolWidget->setLayout(symbolLayout);
- auto controller = SharedCacheController::GetController(*this->m_data);
- if (!controller)
- return;
+ connect(m_symbolTable, &SymbolTableView::activated, this, [=](const QModelIndex& index){
+ auto symbol = m_symbolTable->getSymbolAtRow(index.row());
+ auto dialog = new QMessageBox(this);
- auto image = controller->GetImageContaining(symbol.address);
- if (!image.has_value())
- return;
+ auto controller = SharedCacheController::GetController(*this->m_data);
+ if (!controller)
+ return;
- dialog->setText("Load " + QString::fromStdString(image->name) + "?");
- dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+ auto image = controller->GetImageContaining(symbol.address);
+ if (!image.has_value())
+ return;
- connect(dialog, &QMessageBox::buttonClicked, this, [=](QAbstractButton* button)
- {
- if (button == dialog->button(QMessageBox::Yes))
- loadImagesWithAddr({image->headerAddress});
- });
+ dialog->setText("Load " + QString::fromStdString(image->name) + "?");
+ dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
- dialog->exec();
- });
+ connect(dialog, &QMessageBox::buttonClicked, this, [=](QAbstractButton* button)
+ {
+ if (button == dialog->button(QMessageBox::Yes))
+ loadImagesWithAddr({image->headerAddress});
+ });
- m_triageTabs->addTab(symbolWidget, "Symbols");
- m_triageTabs->setCanCloseTab(symbolWidget, false);
- } // symbolSearch
+ dialog->exec();
+ });
- // Tab: Mappings & Regions
- auto cacheInfoWidget = new QWidget;
- {
- auto cacheInfoLayout = new QVBoxLayout(cacheInfoWidget);
+ m_triageTabs->addTab(symbolWidget, "Symbols");
+ m_triageTabs->setCanCloseTab(symbolWidget, false);
+}
- auto cacheInfoSubwidget = new QWidget;
- auto mappingTable = new FilterableTableView(cacheInfoSubwidget);
- m_mappingModel = new QStandardItemModel(0, 4, mappingTable);
- m_mappingModel->setHorizontalHeaderLabels({"Address", "Size", "File Address", "File Path"});
+void DSCTriageView::initCacheInfoTables()
+{
+ auto cacheInfoWidget = new QWidget;
- // Apply custom column styling
- mappingTable->setItemDelegateForColumn(0, new AddressColorDelegate(mappingTable));
+ auto cacheInfoLayout = new QVBoxLayout(cacheInfoWidget);
- mappingTable->setModel(m_mappingModel);
+ auto cacheInfoSubwidget = new QWidget;
- mappingTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
- mappingTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
- mappingTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
- mappingTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
+ auto mappingTable = new FilterableTableView(cacheInfoSubwidget);
+ m_mappingModel = new QStandardItemModel(0, 4, mappingTable);
+ m_mappingModel->setHorizontalHeaderLabels({"Address", "Size", "File Address", "File Path"});
- mappingTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ // Apply custom column styling
+ mappingTable->setItemDelegateForColumn(0, new AddressColorDelegate(mappingTable));
- mappingTable->setSelectionBehavior(QAbstractItemView::SelectRows);
- mappingTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ mappingTable->setModel(m_mappingModel);
- mappingTable->setSortingEnabled(true);
+ mappingTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
+ mappingTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
+ mappingTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
+ mappingTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
- mappingTable->verticalHeader()->setVisible(false);
+ mappingTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
- auto regionTable = new FilterableTableView(cacheInfoSubwidget);
- m_regionModel = new QStandardItemModel(0, 4, regionTable);
- m_regionModel->setHorizontalHeaderLabels({"Address", "Size", "Type", "Name"});
+ mappingTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ mappingTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
- // Apply custom column styling
- regionTable->setItemDelegateForColumn(0, new AddressColorDelegate(regionTable));
+ mappingTable->setSortingEnabled(true);
- regionTable->setModel(m_regionModel);
+ mappingTable->verticalHeader()->setVisible(false);
- regionTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
- regionTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
- regionTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Interactive);
- regionTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
+ auto regionTable = new FilterableTableView(cacheInfoSubwidget);
+ m_regionModel = new QStandardItemModel(0, 4, regionTable);
+ m_regionModel->setHorizontalHeaderLabels({"Address", "Size", "Type", "Name"});
- regionTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ // Apply custom column styling
+ regionTable->setItemDelegateForColumn(0, new AddressColorDelegate(regionTable));
- regionTable->setSelectionBehavior(QAbstractItemView::SelectRows);
- regionTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ regionTable->setModel(m_regionModel);
- regionTable->setSortingEnabled(true);
+ regionTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
+ regionTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
+ regionTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Interactive);
+ regionTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
- regionTable->verticalHeader()->setVisible(false);
+ regionTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
- auto mappingLabel = new QLabel("Mappings");
- auto mappingFilterEdit = new FilterEdit(mappingTable);
- {
- connect(mappingFilterEdit, &FilterEdit::textChanged, [mappingTable](const QString& filter) {
- mappingTable->setFilter(filter.toStdString());
- });
- }
+ regionTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ regionTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
- auto mappingHeaderLayout = new QHBoxLayout;
- mappingHeaderLayout->addWidget(mappingLabel);
- mappingHeaderLayout->addWidget(mappingFilterEdit);
- mappingHeaderLayout->setAlignment(Qt::AlignJustify);
- mappingHeaderLayout->setSpacing(30);
+ regionTable->setSortingEnabled(true);
- auto regionLabel = new QLabel("Regions");
- auto regionFilterEdit = new FilterEdit(regionTable);
- {
- connect(regionFilterEdit, &FilterEdit::textChanged, [regionTable](const QString& filter) {
- regionTable->setFilter(filter.toStdString());
- });
- }
+ regionTable->verticalHeader()->setVisible(false);
- auto regionHeaderLayout = new QHBoxLayout;
- regionHeaderLayout->addWidget(regionLabel);
- regionHeaderLayout->addWidget(regionFilterEdit);
- regionHeaderLayout->setAlignment(Qt::AlignJustify);
- regionHeaderLayout->setSpacing(30);
+ auto mappingLabel = new QLabel("Mappings");
+ auto mappingFilterEdit = new FilterEdit(mappingTable);
+ {
+ connect(mappingFilterEdit, &FilterEdit::textChanged, [mappingTable](const QString& filter) {
+ mappingTable->setFilter(filter.toStdString());
+ });
+ }
- auto mappingLayout = new QVBoxLayout;
- mappingLayout->addLayout(mappingHeaderLayout);
- mappingLayout->addWidget(mappingTable);
+ auto mappingHeaderLayout = new QHBoxLayout;
+ mappingHeaderLayout->addWidget(mappingLabel);
+ mappingHeaderLayout->addWidget(mappingFilterEdit);
+ mappingHeaderLayout->setAlignment(Qt::AlignJustify);
+ mappingHeaderLayout->setSpacing(30);
- auto regionLayout = new QVBoxLayout;
- regionLayout->addLayout(regionHeaderLayout);
- regionLayout->addWidget(regionTable);
+ auto regionLabel = new QLabel("Regions");
+ auto regionFilterEdit = new FilterEdit(regionTable);
+ {
+ connect(regionFilterEdit, &FilterEdit::textChanged, [regionTable](const QString& filter) {
+ regionTable->setFilter(filter.toStdString());
+ });
+ }
- cacheInfoLayout->addLayout(mappingLayout);
- cacheInfoLayout->addLayout(regionLayout);
+ auto regionHeaderLayout = new QHBoxLayout;
+ regionHeaderLayout->addWidget(regionLabel);
+ regionHeaderLayout->addWidget(regionFilterEdit);
+ regionHeaderLayout->setAlignment(Qt::AlignJustify);
+ regionHeaderLayout->setSpacing(30);
- m_triageTabs->addTab(cacheInfoWidget, "Mappings & Regions");
- m_triageTabs->setCanCloseTab(cacheInfoWidget, false);
- } // cacheInfoSection
+ auto mappingLayout = new QVBoxLayout;
+ mappingLayout->addLayout(mappingHeaderLayout);
+ mappingLayout->addWidget(mappingTable);
- m_layout = new QVBoxLayout(this);
- m_layout->addWidget(m_triageTabs);
- setLayout(m_layout);
+ auto regionLayout = new QVBoxLayout;
+ regionLayout->addLayout(regionHeaderLayout);
+ regionLayout->addWidget(regionTable);
- // In case we have already initialized the controller (user has opened this view type again)
- // we will call refresh data. If this is the first triage view constructed (i.e. before view init) then this
- // will do nothing.
- RefreshData();
+ cacheInfoLayout->addLayout(mappingLayout);
+ cacheInfoLayout->addLayout(regionLayout);
- m_triageTabs->selectWidget(defaultWidget);
+ m_triageTabs->addTab(cacheInfoWidget, "Mappings & Regions");
+ m_triageTabs->setCanCloseTab(cacheInfoWidget, false);
}
-DSCTriageView::~DSCTriageView()
-{
- UIContext::unregisterNotification(this);
-}
QFont DSCTriageView::getFont()
{
return getMonospaceFont(this);
}
+
BinaryViewRef DSCTriageView::getData()
{
return m_data;
}
+
bool DSCTriageView::navigate(uint64_t offset)
{
// TODO: We have to set this to true otherwise view restore does not pickup this view.
return true;
}
+
uint64_t DSCTriageView::getCurrentOffset()
{
return 0;
}
+
SelectionInfoForXref DSCTriageView::getSelectionForXref()
{
// TODO: If we are in the symbols view we _can_ actually show a useful xref to the selected symbols.
@@ -461,12 +477,14 @@ SelectionInfoForXref DSCTriageView::getSelectionForXref()
return selection;
}
+
void DSCTriageView::OnAfterOpenFile(UIContext *context, FileContext *file, ViewFrame *frame)
{
RefreshData();
UIContextNotification::OnAfterOpenFile(context, file, frame);
}
+
// Called when shared cache information has changed.
void DSCTriageView::RefreshData()
{
@@ -517,6 +535,7 @@ void DSCTriageView::RefreshData()
m_symbolTable->populateSymbols(*m_data);
}
+
void DSCTriageView::setImageLoaded(const uint64_t imageHeaderAddr)
{
// Go through the m_loadImageModel and find the image associated with the address
diff --git a/view/sharedcache/ui/dsctriage.h b/view/sharedcache/ui/dsctriage.h
index b1e98f6d..a16457e3 100644
--- a/view/sharedcache/ui/dsctriage.h
+++ b/view/sharedcache/ui/dsctriage.h
@@ -1,7 +1,3 @@
-//
-// Created by kat on 8/15/24.
-//
-
#include <binaryninjaapi.h>
#include <QItemDelegate>
#include <QStyledItemDelegate>
@@ -19,6 +15,7 @@
#ifndef BINARYNINJA_DSCTRIAGE_H
#define BINARYNINJA_DSCTRIAGE_H
+
class AddressColorDelegate : public QStyledItemDelegate
{
@@ -37,6 +34,65 @@ public:
}
};
+
+class LoadedDelegate : public QItemDelegate
+{
+Q_OBJECT
+
+public:
+ explicit LoadedDelegate(QObject* parent = nullptr) : QItemDelegate(parent) {}
+
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override
+ {
+ if (!index.isValid())
+ return;
+
+ painter->save();
+
+ // Highlight if the item is selected
+ if (option.state & QStyle::State_Selected)
+ painter->fillRect(option.rect, option.palette.highlight());
+
+ // "1" is the indicator that its loaded.
+ if (index.data(Qt::DisplayRole).toString() == "1")
+ {
+ QPixmap loadedIcon;
+ pixmapForBWMaskIcon(":/icons/images/check.png", &loadedIcon, SidebarHeaderTextColor);
+ if (!loadedIcon.isNull())
+ {
+ QSize pixmapSize(20, 20);
+ QPixmap scaledPixmap = loadedIcon.scaled(pixmapSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+
+ // Calculate the rectangle for centering the pixmap
+ int x = option.rect.x() + (option.rect.width() - scaledPixmap.width()) / 2; // Center horizontally
+ int y = option.rect.y() + (option.rect.height() - scaledPixmap.height()) / 2; // Center vertically
+ QRect iconRect(x, y, scaledPixmap.width(), scaledPixmap.height());
+
+ // Draw the pixmap
+ painter->drawPixmap(iconRect, scaledPixmap);
+ }
+ }
+
+ painter->restore();
+ }
+
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override
+ {
+ Q_UNUSED(option);
+ Q_UNUSED(index);
+ return {50, 24};
+ }
+
+ void setEditorData(QWidget *editor, const QModelIndex &index) const override
+ {
+ Q_UNUSED(editor);
+ Q_UNUSED(index);
+ }
+};
+
+
class FilterableTableView : public QTableView, public FilterTarget {
Q_OBJECT
@@ -119,6 +175,7 @@ signals:
void filterTextChanged(const QString& text);
};
+
class DSCTriageView : public QWidget, public View, public UIContextNotification
{
BinaryViewRef m_data;
@@ -148,6 +205,12 @@ public:
void OnAfterOpenFile(UIContext* context, FileContext* file, ViewFrame* frame) override;
void RefreshData();
void setImageLoaded(uint64_t imageHeaderAddr);
+
+private:
+ void loadImagesWithAddr(const std::vector<uint64_t>& addresses, bool includeDependencies = false);
+ QWidget* initImageTable();
+ void initSymbolTable();
+ void initCacheInfoTables();
};
@@ -160,61 +223,4 @@ public:
static void Register();
};
-class LoadedDelegate : public QItemDelegate
-{
- Q_OBJECT
-
-public:
- explicit LoadedDelegate(QObject* parent = nullptr) : QItemDelegate(parent) {}
-
- void paint(QPainter *painter, const QStyleOptionViewItem &option,
- const QModelIndex &index) const override
- {
- if (!index.isValid())
- return;
-
- painter->save();
-
- // Highlight if the item is selected
- if (option.state & QStyle::State_Selected)
- painter->fillRect(option.rect, option.palette.highlight());
-
- // "1" is the indicator that its loaded.
- if (index.data(Qt::DisplayRole).toString() == "1")
- {
- QPixmap loadedIcon;
- pixmapForBWMaskIcon(":/icons/images/check.png", &loadedIcon, SidebarHeaderTextColor);
- if (!loadedIcon.isNull())
- {
- QSize pixmapSize(20, 20);
- QPixmap scaledPixmap = loadedIcon.scaled(pixmapSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
-
- // Calculate the rectangle for centering the pixmap
- int x = option.rect.x() + (option.rect.width() - scaledPixmap.width()) / 2; // Center horizontally
- int y = option.rect.y() + (option.rect.height() - scaledPixmap.height()) / 2; // Center vertically
- QRect iconRect(x, y, scaledPixmap.width(), scaledPixmap.height());
-
- // Draw the pixmap
- painter->drawPixmap(iconRect, scaledPixmap);
- }
- }
-
- painter->restore();
- }
-
- QSize sizeHint(const QStyleOptionViewItem &option,
- const QModelIndex &index) const override
- {
- Q_UNUSED(option);
- Q_UNUSED(index);
- return {50, 24};
- }
-
- void setEditorData(QWidget *editor, const QModelIndex &index) const override
- {
- Q_UNUSED(editor);
- Q_UNUSED(index);
- }
-};
-
#endif // BINARYNINJA_DSCTRIAGE_H
diff --git a/view/sharedcache/ui/symboltable.cpp b/view/sharedcache/ui/symboltable.cpp
index 0b006fbc..1df7d529 100644
--- a/view/sharedcache/ui/symboltable.cpp
+++ b/view/sharedcache/ui/symboltable.cpp
@@ -102,7 +102,8 @@ void SymbolTableModel::setFilter(std::string text)
if (((std::string_view)symbol.name).find(m_filter) != std::string::npos)
m_modelSymbols.push_back(symbol);
m_modelSymbols.shrink_to_fit();
- } else
+ }
+ else
{
m_modelSymbols = m_preparedSymbols;
}
@@ -112,7 +113,7 @@ void SymbolTableModel::setFilter(std::string text)
SymbolTableView::SymbolTableView(QWidget* parent)
- : m_model(new SymbolTableModel(this)) {
+ : QTableView(parent), m_model(new SymbolTableModel(this)) {
// Set up the filter model
setModel(m_model);