611f3da62822307dc1043944f944f6a079a60ed0
[frida/frida.git] / src / gui / Mainwindow.cxx
1 #include "Mainwindow.hxx"
2 #include "qt.hxx"
3 #include "disassembler/llvm/LLVMDisassembler.hxx"
4
5 #include "widgets/CFGScene.hxx"
6 #include "dialogs/NewFunctionDialog.hxx"
7 #include "dialogs/RenameFunctionDialog.hxx"
8
9 #include <sstream>
10
11 namespace {
12 BasicBlockWidget *
13 local__add_basic_block(BasicBlock * block, Disassembler * dis,
14 Mainwindow * mainwindow,
15 std::map<uint64_t, BasicBlockWidget*>& known_blocks,
16 CFGScene * scene, uint64_t starty, uint64_t startx);
17 }
18
19 Mainwindow::Mainwindow(InformationManager* mgr)
20 : manager(mgr)
21 , logger(log4cxx::Logger::getLogger("Mainwindow")) {
22 openAction = new QAction(tr("&Open"), this);
23 // saveAction = new QAction(tr("&Save"), this);
24 exitAction = new QAction(tr("E&xit"), this);
25
26 connect(openAction, SIGNAL(triggered()),
27 this, SLOT(open()));
28 // connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
29 connect(exitAction, SIGNAL(triggered()),
30 qApp, SLOT(quit()));
31
32 fileMenu = menuBar()->addMenu(tr("&File"));
33 fileMenu->addAction(openAction);
34 // fileMenu->addAction(saveAction);
35 fileMenu->addSeparator();
36 fileMenu->addAction(exitAction);
37
38 scripting = new ScriptingDock(tr("Scripting"), this);
39 scripting->setAllowedAreas(Qt::BottomDockWidgetArea);
40 addDockWidget(Qt::BottomDockWidgetArea, scripting);
41
42 listWidget = new QListWidget();
43 listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
44 connect(listWidget, SIGNAL(customContextMenuRequested(const QPoint&)),
45 this, SLOT(showListContextMenu(const QPoint&)));
46
47 stackedWidget = new QStackedWidget();
48 dockWidget = new QDockWidget(tr("Functions"), this);
49 dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea |
50 Qt::RightDockWidgetArea);
51 dockWidget->setWidget(listWidget);
52 addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
53 setCentralWidget(stackedWidget);
54
55 connect(listWidget, SIGNAL(currentRowChanged(int)),
56 this, SLOT(switchMainPlane(int)));
57
58 setWindowTitle(tr("FRIDA"));
59
60 mgr->connect_new_function_signal([&] (Function* fun) {addFunction(fun);});
61 mgr->connect_new_dyn_symbol_signal([&] (const std::string& name) {
62 auto item = new QListWidgetItem(name.c_str(), listWidget);
63 item->setBackground(QBrush(QColor(0xff, 0xdd, 0xdd)));
64 });
65 }
66
67 void Mainwindow::quit()
68 {
69 QMessageBox messageBox;
70 messageBox.setWindowTitle(tr("Frida"));
71 messageBox.setText(tr("Do you really want to quit?"));
72 messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
73 messageBox.setDefaultButton(QMessageBox::No);
74 if (messageBox.exec() == QMessageBox::Yes)
75 qApp->quit();
76 }
77
78 void Mainwindow::open() {
79 QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
80 tr("Binaries (*)"));
81
82 manager->reset(fileName.toStdString());
83 }
84
85 void Mainwindow::switchMainPlaneToAddress(uint64_t address) {
86 if (objects_list_by_address.find(address) != objects_list_by_address.end()) {
87 LOG4CXX_DEBUG(logger, "Switching to function " << std::hex << address);
88 QListWidgetItem * item = objects_list_by_address[address];
89 listWidget->setCurrentItem(item);
90 stackedWidget->setCurrentWidget(objects_list[item]);
91 } else {
92 LOG4CXX_DEBUG(logger, "No function at " << std::hex << address
93 << " -- it's probably an imported Symbol");
94 }
95 }
96
97 void Mainwindow::switchMainPlane(int index) {
98 stackedWidget->setCurrentWidget(objects_list[listWidget->currentItem()]);
99 }
100
101 void Mainwindow::showListContextMenu(const QPoint& point) {
102 QListWidgetItem * item = listWidget->itemAt(point);
103 QMenu menu(this);
104 if (item) {
105 QAction * act = menu.addAction("Rename Function");
106 connect(act, &QAction::triggered, [=]() {this->renameFunction(item);});
107 } else {
108 QAction * act = menu.addAction("AddFunction");
109 connect(act, SIGNAL(triggered()), this, SLOT(requestNewFunction()));
110 }
111 menu.exec(listWidget->mapToGlobal(point));
112 }
113
114 void Mainwindow::requestNewFunction() {
115 NewFunctionDialog dialog;
116 int result = dialog.exec();
117 if (QDialog::Accepted == result) {
118 LOG4CXX_DEBUG(logger, "requesting Function at " << std::hex << dialog.result());
119 manager->getDisassembler()->disassembleFunctionAt(dialog.result());
120 } else {
121 LOG4CXX_DEBUG(logger, "requestNewFunction aborted");
122 }
123 }
124
125 void Mainwindow::renameFunction(QListWidgetItem * item) {
126 RenameFunctionDialog dialog;
127 int result = dialog.exec();
128 if (QDialog::Accepted == result) {
129 LOG4CXX_DEBUG(logger, "renaming Function" << item->text().toStdString()
130 << " to " << dialog.result().toStdString());
131 item->setText(dialog.result());
132 } else {
133 LOG4CXX_DEBUG(logger, "renameFunction aborted");
134 }
135 }
136
137 void Mainwindow::addFunction(Function* fun) {
138 if (functions.find(fun) != functions.end())
139 return;
140
141 functions.insert(fun);
142
143 QTabWidget * w = new QTabWidget();
144
145 // CFG
146 CFGScene * scene = new CFGScene;
147
148 Disassembler * dis = manager->getDisassembler();
149 BasicBlock * block = dis->getBasicBlock(fun->getStartAddress());
150
151 uint64_t start_address(std::numeric_limits<uint64_t>::max());
152 for (auto b : fun->blocks()) {
153 if (b.first < start_address)
154 start_address = b.first;
155 }
156
157 local__add_basic_block(block, manager->getDisassembler(), this,
158 blocks, scene, start_address, 100);
159
160 QGraphicsView * view = new QGraphicsView(scene);
161 w->addTab(view, "CFG");
162
163 // Listing
164 QTableWidget * t = new QTableWidget();
165 t->setColumnCount(3);
166 t->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
167
168 w->addTab(t, "Listing");
169
170 QListWidgetItem * item = new QListWidgetItem(fun->getName().c_str(), listWidget);
171 stackedWidget->addWidget(w);
172 objects_list.insert(std::make_pair(item, w));
173 LOG4CXX_DEBUG(logger, "Adding function widget at " << std::hex
174 << fun->getStartAddress());
175 objects_list_by_address.insert(std::make_pair(fun->getStartAddress(), item));
176 }
177
178 namespace {
179 BasicBlockWidget *
180 local__add_basic_block(BasicBlock * block, Disassembler * dis,
181 Mainwindow * mainwindow,
182 std::map<uint64_t, BasicBlockWidget*>& known_blocks,
183 CFGScene * scene, uint64_t starty, uint64_t startx) {
184
185 decltype(known_blocks.begin()) old;
186 if ((old = known_blocks.find(block->getStartAddress())) != known_blocks.end())
187 return old->second;
188
189 std::stringstream s;
190 s << "BLOCK_" << std::hex << block->getStartAddress()
191 << "_" << block->getEndAddress();
192 BasicBlockWidget * widget = new BasicBlockWidget(s.str().c_str(),
193 block, mainwindow);
194
195 known_blocks.insert(std::make_pair(block->getStartAddress(), widget));
196
197 scene->addItem(widget);
198 widget->setFlag(QGraphicsItem::ItemIsMovable, true);
199 widget->moveBy(100*startx, block->getStartAddress() - starty);
200
201 dis->printEachInstruction(block->getStartAddress(),
202 block->getEndAddress(),
203 [&](uint8_t* bytes,
204 size_t byte_count,
205 const std::string& line,
206 const std::string& ref) {
207 widget->addItem(bytes, byte_count,
208 line.c_str() + 1, // remove \t
209 ref.c_str());
210 });
211
212 BasicBlockWidget *tmp, *nextl(NULL), *nextr(NULL);
213 BasicBlock * tmpblock;
214 if (block->getNextBlock(0) != 0) {
215 int xshift = 0;
216 if (block->getNextBlock(1) != 0)
217 xshift = 1;
218 tmpblock = dis->getBasicBlock(block->getNextBlock(0));
219 tmp = local__add_basic_block(tmpblock, dis,
220 mainwindow,
221 known_blocks,
222 scene, starty, startx+xshift);
223 nextl = tmp;
224 tmp->addPrevious(widget);
225 }
226 if (block->getNextBlock(1) != 0) {
227 tmpblock = dis->getBasicBlock(block->getNextBlock(1));
228 tmp = local__add_basic_block(tmpblock, dis,
229 mainwindow,
230 known_blocks,
231 scene, starty, startx-1);
232 nextr = tmp;
233 tmp->addPrevious(widget);
234 }
235 widget->addNext(nextl, nextr);
236 return widget;
237 }
238 }