Decompiler Documentation + Clean up (#2374)

This commit is contained in:
NIRMAL MANOJ C 2020-08-15 13:31:27 +05:30
parent 1c86f54c95
commit b7d1059a1b
4 changed files with 206 additions and 75 deletions

View File

@ -17,11 +17,11 @@
DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWindow)
: QMenu(parent),
mainWindow(mainWindow),
curHighlightedWord(QString()),
offset(0),
decompiledFunctionAddress(RVA_INVALID),
isTogglingBreakpoints(false),
mainWindow(mainWindow),
annotationHere(nullptr),
actionCopy(tr("Copy"), this),
actionCopyInstructionAddress(tr("Copy instruction address (<address>)"), this),
@ -48,11 +48,11 @@ DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWi
setActionAddComment();
setActionDeleteComment();
setActionXRefs();
setActionRenameThingHere();
setActionDeleteName();
setActionXRefs();
setActionEditFunctionVariables();
addSeparator();
@ -73,34 +73,37 @@ DecompilerContextMenu::~DecompilerContextMenu()
void DecompilerContextMenu::setAnnotationHere(RCodeAnnotation *annotation)
{
this->annotationHere = annotation;
annotationHere = annotation;
}
void DecompilerContextMenu::setCurHighlightedWord(QString word)
{
this->curHighlightedWord = word;
curHighlightedWord = word;
}
void DecompilerContextMenu::setOffset(RVA offset)
void DecompilerContextMenu::setOffset(RVA newOffset)
{
this->offset = offset;
// this->actionSetFunctionVarTypes.setVisible(true);
offset = newOffset;
}
void DecompilerContextMenu::setDecompiledFunctionAddress(RVA functionAddr)
{
this->decompiledFunctionAddress = functionAddr;
decompiledFunctionAddress = functionAddr;
}
void DecompilerContextMenu::setFirstOffsetInLine(RVA firstOffset)
{
this->firstOffsetInLine = firstOffset;
firstOffsetInLine = firstOffset;
}
RVA DecompilerContextMenu::getFirstOffsetInLine()
{
return firstOffsetInLine;
}
void DecompilerContextMenu::setAvailableBreakpoints(QVector<RVA> offsetList)
{
this->availableBreakpoints = offsetList;
availableBreakpoints = offsetList;
}
void DecompilerContextMenu::setupBreakpointsInLineMenu()
@ -130,12 +133,12 @@ void DecompilerContextMenu::setShortcutContextInActions(QMenu *menu)
void DecompilerContextMenu::setIsTogglingBreakpoints(bool isToggling)
{
this->isTogglingBreakpoints = isToggling;
isTogglingBreakpoints = isToggling;
}
bool DecompilerContextMenu::getIsTogglingBreakpoints()
{
return this->isTogglingBreakpoints;
return isTogglingBreakpoints;
}
void DecompilerContextMenu::aboutToHideSlot()
@ -169,7 +172,6 @@ void DecompilerContextMenu::aboutToShowSlot()
actionDeleteComment.setVisible(false);
}
setupBreakpointsInLineMenu();
// Only show debug options if we are currently debugging
@ -184,7 +186,6 @@ void DecompilerContextMenu::aboutToShowSlot()
} else {
actionToggleBreakpoint.setText(tr("Remove all breakpoints in line"));
}
if (numberOfBreakpoints > 1) {
actionAdvancedBreakpoint.setMenu(breakpointsInLineMenu);
} else {
@ -198,7 +199,7 @@ void DecompilerContextMenu::aboutToShowSlot()
if (!annotationHere
|| annotationHere->type ==
R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) { // To be considered as invalid
R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) { // If constant, don't show rename and targeted show-in
actionRenameThingHere.setVisible(false);
copySeparator->setVisible(false);
} else {
@ -219,7 +220,7 @@ void DecompilerContextMenu::aboutToShowSlot()
}
actionCopyInstructionAddress.setText(tr("Copy instruction address (%1)").arg(RAddressString(
offset)));
if (annotationHere && r_annotation_is_reference(annotationHere)) {
if (isReference()) {
actionCopyReferenceAddress.setVisible(true);
RVA referenceAddr = annotationHere->reference.offset;
RFlagItem *flagDetails = r_flag_get_i(Core()->core()->flags, referenceAddr);
@ -258,7 +259,6 @@ void DecompilerContextMenu::aboutToShowSlot()
// Set up actions
void DecompilerContextMenu::setActionCopy() // Set all three copy actions
{
connect(&actionCopy, &QAction::triggered, this, &DecompilerContextMenu::actionCopyTriggered);
@ -386,7 +386,7 @@ void DecompilerContextMenu::actionDeleteCommentTriggered()
void DecompilerContextMenu::actionRenameThingHereTriggered()
{
if (!annotationHere) {
if (!annotationHere || annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) {
return;
}
RCoreLocked core = Core()->core();
@ -410,7 +410,6 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
Core()->renameFunction(func_addr, newName);
}
}
} else if (type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE) {
RVA var_addr = annotationHere->reference.offset;
RFlagItem *flagDetails = r_flag_get_i(core->flags, var_addr);
@ -467,7 +466,7 @@ void DecompilerContextMenu::actionEditFunctionVariablesTriggered()
void DecompilerContextMenu::actionXRefsTriggered()
{
if (!annotationHere || !r_annotation_is_reference(annotationHere)) {
if (!isReference()) {
return;
}
XrefsDialog dialog(mainWindow, nullptr);
@ -551,9 +550,7 @@ void DecompilerContextMenu::updateTargetMenuActions()
}
showTargetMenuActions.clear();
RCoreLocked core = Core()->core();
if (annotationHere && (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME)) {
if (isReference()) {
QString name;
QMenu *menu;
if (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE
@ -580,6 +577,11 @@ void DecompilerContextMenu::updateTargetMenuActions()
}
}
bool DecompilerContextMenu::isReference()
{
return (annotationHere && r_annotation_is_reference(annotationHere));
}
bool DecompilerContextMenu::isFunctionVariable()
{
return (annotationHere && r_annotation_is_variable(annotationHere));

View File

@ -7,6 +7,8 @@
#include <r_util/r_annotated_code.h>
class MainWindow;
class DecompilerContextMenu : public QMenu
{
Q_OBJECT
@ -17,18 +19,18 @@ public:
bool getIsTogglingBreakpoints();
void setAnnotationHere(RCodeAnnotation *annotation);
RVA getFirstOffsetInLine();
signals:
void copy();
public slots:
void setCurHighlightedWord(QString word);
void setOffset(RVA offset);
void setOffset(RVA newOffset);
void setDecompiledFunctionAddress(RVA functionAddr);
void setFirstOffsetInLine(RVA firstOffset);
void setAvailableBreakpoints(QVector<RVA> offsetList);
private slots:
void aboutToShowSlot();
void aboutToHideSlot();
@ -55,16 +57,30 @@ private slots:
private:
// Private variables
MainWindow *mainWindow;
QString curHighlightedWord;
RVA offset;
RVA decompiledFunctionAddress;
/**
* Lowest offset among all offsets present in the line under cursor in the decompiler widget.
*/
RVA firstOffsetInLine;
/**
* When the actionToggleBreakpoint has been triggered, and it hasn't finished executing,
* the value of this variable will be true, otherwise false
*/
bool isTogglingBreakpoints;
/**
* List of the offsets of all the breakpoints (enabled and disabled) that are present in the line under cursor.
*/
QVector<RVA> availableBreakpoints;
MainWindow *mainWindow;
/**
* Context-related annotation for the data under cursor in the decompiler widget.
* If such an annotation doesn't exist, its value is nullptr.
*/
RCodeAnnotation *annotationHere;
// Actions and menus in the context menu
QAction actionCopy;
QAction actionCopyInstructionAddress;
QAction actionCopyReferenceAddress;
@ -94,6 +110,12 @@ private:
QAction actionSetPC;
// Private Functions
/**
* @brief Sets the shortcut context in all the actions contained
* in the specified QMenu to Qt::WidgetWithChildrenShortcut.
*
* @param menu - QMenu specified
*/
void setShortcutContextInActions(QMenu *menu);
void setupBreakpointsInLineMenu();
void setIsTogglingBreakpoints(bool isToggling);
@ -123,9 +145,35 @@ private:
void addBreakpointMenu();
void addDebugMenu();
/**
* @brief Updates targeted "Show in" menu.
*
* Removes all actions from the existing targeted "show in" menu. If annotationHere
* represents an item that has an address assigned to it, insert actions compatible with the
* type of this item in the targeted "Show in" menu.
*/
void updateTargetMenuActions();
/**
* @brief Check if annotationHere is a reference (function name,
* global variable, constant variable with an address).
*
* @return True if annotationHere is a reference, otherwise false.
*/
bool isReference();
/**
* @brief Check if annotationHere is a function variable
* (local variable or function parameter).
*
* @return True if annotationHere is a function variable, otherwise false.
*/
bool isFunctionVariable();
/**
* @brief Check if the function variable annotated by annotationHere is
* present in radare2.
*
* @return True if the variable is present, otherwise false
*/
bool variablePresentInR2();
};

View File

@ -31,10 +31,8 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
&r_annotated_code_free)
{
ui->setupUi(this);
syntaxHighlighter = Config()->createSyntaxHighlighter(ui->textEdit->document());
// Event filter to intercept double clicks in the textbox
// Event filter to intercept double click and right click in the textbox
ui->textEdit->viewport()->installEventFilter(this);
setupFonts();
@ -48,11 +46,9 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
connect(ui->refreshButton, &QAbstractButton::clicked, this, [this]() {
doRefresh();
});
refreshDeferrer = createRefreshDeferrer([this]() {
doRefresh();
});
autoRefreshEnabled = Config()->getDecompilerAutoRefreshEnabled();
ui->autoRefreshCheckBox->setChecked(autoRefreshEnabled);
setAutoRefresh(autoRefreshEnabled);
@ -75,10 +71,8 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
}
connect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
}
decompilerSelectionEnabled = decompilers.size() > 1;
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
if (decompilers.isEmpty()) {
ui->textEdit->setPlainText(tr("No Decompiler available."));
}
@ -90,7 +84,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
connect(Core(), &CutterCore::seekChanged, this, &DecompilerWidget::seekChanged);
ui->textEdit->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->textEdit, &QWidget::customContextMenuRequested,
this, &DecompilerWidget::showDisasContextMenu);
this, &DecompilerWidget::showDecompilerContextMenu);
connect(Core(), &CutterCore::breakpointsChanged, this, &DecompilerWidget::updateBreakpoints);
addActions(mCtxMenu->actions());
@ -147,13 +141,13 @@ void DecompilerWidget::updateRefreshButton()
}
}
static ut64 offsetForPosition(RAnnotatedCode &codeDecompiled, size_t pos)
ut64 DecompilerWidget::offsetForPosition(size_t pos)
{
size_t closestPos = SIZE_MAX;
ut64 closestOffset = UT64_MAX;
void *annotationi;
r_vector_foreach(&codeDecompiled.annotations, annotationi) {
RCodeAnnotation *annotation = (RCodeAnnotation *)annotationi;
ut64 closestOffset = mCtxMenu->getFirstOffsetInLine();
void *iter;
r_vector_foreach(&code->annotations, iter) {
RCodeAnnotation *annotation = (RCodeAnnotation *)iter;
if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET || annotation->start > pos
|| annotation->end <= pos) {
continue;
@ -167,13 +161,13 @@ static ut64 offsetForPosition(RAnnotatedCode &codeDecompiled, size_t pos)
return closestOffset;
}
static size_t positionForOffset(RAnnotatedCode &codeDecompiled, ut64 offset)
size_t DecompilerWidget::positionForOffset(ut64 offset)
{
size_t closestPos = SIZE_MAX;
ut64 closestOffset = UT64_MAX;
void *annotationi;
r_vector_foreach(&codeDecompiled.annotations, annotationi) {
RCodeAnnotation *annotation = (RCodeAnnotation *)annotationi;
void *iter;
r_vector_foreach(&code->annotations, iter) {
RCodeAnnotation *annotation = (RCodeAnnotation *)iter;
if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET || annotation->offset.offset > offset) {
continue;
}
@ -216,13 +210,13 @@ void DecompilerWidget::gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size
size_t endPos)
{
RVA firstOffset = RVA_MAX;
void *annotationi;
r_vector_foreach(&codeDecompiled.annotations, annotationi) {
RCodeAnnotation *annotation = (RCodeAnnotation *)annotationi;
void *iter;
r_vector_foreach(&codeDecompiled.annotations, iter) {
RCodeAnnotation *annotation = (RCodeAnnotation *)iter;
if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET) {
continue;
}
if ((startPos <= annotation->start && annotation->start < endPos) || (startPos <= annotation->end
if ((startPos <= annotation->start && annotation->start < endPos) || (startPos < annotation->end
&& annotation->end < endPos)) {
firstOffset = (annotation->offset.offset < firstOffset) ? annotation->offset.offset : firstOffset;
}
@ -231,7 +225,7 @@ void DecompilerWidget::gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size
QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr);
QVector<RVA> offsetList;
for (auto bpOffset : functionBreakpoints) {
size_t pos = positionForOffset(*code, bpOffset);
size_t pos = positionForOffset(bpOffset);
if (startPos <= pos && pos <= endPos) {
offsetList.push_back(bpOffset);
}
@ -245,26 +239,21 @@ void DecompilerWidget::doRefresh(RVA addr)
if (!refreshDeferrer->attemptRefresh(nullptr)) {
return;
}
if (ui->decompilerComboBox->currentIndex() < 0) {
return;
}
Decompiler *dec = getCurrentDecompiler();
if (!dec) {
return;
}
if (dec->isRunning()) {
decompilerWasBusy = true;
return;
}
if (addr == RVA_INVALID) {
ui->textEdit->setPlainText(tr("Click Refresh to generate Decompiler from current offset."));
return;
}
// Clear all selections since we just refreshed
ui->textEdit->setExtraSelections({});
previousFunctionAddr = decompiledFunctionAddr;
@ -287,11 +276,10 @@ void DecompilerWidget::refreshDecompiler()
QTextCursor DecompilerWidget::getCursorForAddress(RVA addr)
{
size_t pos = positionForOffset(*code, addr);
size_t pos = positionForOffset(addr);
if (pos == SIZE_MAX || pos == 0) {
return QTextCursor();
}
QTextCursor cursor = ui->textEdit->textCursor();
cursor.setPosition(pos);
return cursor;
@ -339,9 +327,9 @@ void DecompilerWidget::decompilationFinished(RAnnotatedCode *codeDecompiled)
void DecompilerWidget::setAnnotationsAtCursor(size_t pos)
{
RCodeAnnotation *annotationAtPos = nullptr;
void *annotationi;
r_vector_foreach(&this->code->annotations, annotationi) {
RCodeAnnotation *annotation = (RCodeAnnotation *)annotationi;
void *iter;
r_vector_foreach(&this->code->annotations, iter) {
RCodeAnnotation *annotation = (RCodeAnnotation *)iter;
if (annotation->type == R_CODE_ANNOTATION_TYPE_OFFSET ||
annotation->type == R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT ||
annotation->start > pos || annotation->end <= pos) {
@ -381,10 +369,9 @@ void DecompilerWidget::cursorPositionChanged()
size_t pos = ui->textEdit->textCursor().position();
setAnnotationsAtCursor(pos);
setInfoForBreakpoints();
RVA offset = offsetForPosition(*code, pos);
RVA offset = offsetForPosition(pos);
if (offset != RVA_INVALID && offset != Core()->getOffset()) {
seekFromCursor = true;
Core()->seek(offset);
@ -399,7 +386,6 @@ void DecompilerWidget::seekChanged()
if (seekFromCursor) {
return;
}
if (autoRefreshEnabled) {
auto fcnAddr = Core()->getFunctionStart(Core()->getOffset());
if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) {
@ -407,14 +393,13 @@ void DecompilerWidget::seekChanged()
return;
}
}
updateCursorPosition();
}
void DecompilerWidget::updateCursorPosition()
{
RVA offset = Core()->getOffset();
size_t pos = positionForOffset(*code, offset);
size_t pos = positionForOffset(offset);
if (pos == SIZE_MAX) {
return;
}
@ -465,7 +450,7 @@ void DecompilerWidget::colorsUpdatedSlot()
{
}
void DecompilerWidget::showDisasContextMenu(const QPoint &pt)
void DecompilerWidget::showDecompilerContextMenu(const QPoint &pt)
{
mCtxMenu->exec(ui->textEdit->mapToGlobal(pt));
}
@ -473,7 +458,7 @@ void DecompilerWidget::showDisasContextMenu(const QPoint &pt)
void DecompilerWidget::seekToReference()
{
size_t pos = ui->textEdit->textCursor().position();
RVA offset = offsetForPosition(*code, pos);
RVA offset = offsetForPosition(pos);
seekable->seekToReference(offset);
}
@ -520,7 +505,6 @@ void DecompilerWidget::highlightBreakpoints()
if (bp == RVA_INVALID) {
continue;;
}
cursor = getCursorForAddress(bp);
if (!cursor.isNull()) {
// Use a Block formatting since these lines are not updated frequently as selections and PC

View File

@ -28,16 +28,30 @@ public:
explicit DecompilerWidget(MainWindow *main);
~DecompilerWidget();
public slots:
void showDisasContextMenu(const QPoint &pt);
void showDecompilerContextMenu(const QPoint &pt);
void highlightPC();
private slots:
/**
* @brief Copy to clipboard what's needed depending on the state of text widget.
*
* @note If something is selected in the text widget, copy selection.
* If something is highlighted, copy highlighted word.
* Otherwise, copy the line under cursor.
*/
void copy();
void fontsUpdatedSlot();
void colorsUpdatedSlot();
void refreshDecompiler();
void decompilerSelected();
void cursorPositionChanged();
/**
* @brief When the synced seek is changed, this refreshes the decompiler widget if needed.
*
* Decompiler widget is not refreshed in the following two cases
* - Seek changed to an offset contained in the decompiled function.
* - Auto-refresh is disabled.
*/
void seekChanged();
void decompilationFinished(RAnnotatedCode *code);
@ -51,7 +65,7 @@ private:
bool autoRefreshEnabled;
/**
* True if doRefresh() was called, but the decompiler was still running
* True if doRefresh() was called, but the decompiler was still running.
* This means, after the decompiler has finished, it should be refreshed immediately.
*/
bool decompilerWasBusy;
@ -65,16 +79,71 @@ private:
Decompiler *getCurrentDecompiler();
/**
* @brief Enable/Disable auto refresh as per the specified boolean value
*
* @param enabled
*/
void setAutoRefresh(bool enabled);
/**
* @brief Calls the function doRefresh() if auto-refresh is enabled.
*/
void doAutoRefresh();
/**
* @brief Refreshes the decompiler.
*
* - This does the following if the specified offset is valid
* - Decompile function that contains the specified offset.
* - Clears all selections stored for highlighting purposes.
* - Reset previousFunctionAddr with the current function's address
* and decompiledFunctionAddr with the address of the function that
* was decompiled.
* - If the offset is invalid, error message is shown in the text widget.
*
* @param addr Specified offset/offset in sync.
*/
void doRefresh(RVA addr = Core()->getOffset());
void updateRefreshButton();
/**
* @brief Update fonts
*/
void setupFonts();
/**
* @brief Update highlights in the text widget.
*
* These include respective highlights for:
* - Line under cursor
* - Word under cursor
* - Program Counter(PC) while debugging
*/
void updateSelection();
/**
* @brief Connect/Disconnect SIGNAL-SLOT connection that deals with changes in cursor position.
*
* If the argument is true, then disconnect the SIGNAL-SLOT connection
* that changes the view as cursor position gets changed in the text widget.
* Otherwise, connect the corresponding signal with slot.
*
* @param disconnect
*/
void connectCursorPositionChanged(bool disconnect);
/**
* @brief Find the current global offset in sync and update cursor
* to the position specified by this offset (found using positionForOffset() )
*/
void updateCursorPosition();
QString getWindowTitle() const override;
/**
* @brief Event filter that intercept the following events:
* 1. Double click
* 2. Right click
*
* @param obj
* @param event
* @return
*/
bool eventFilter(QObject *obj, QEvent *event) override;
/**
@ -104,21 +173,49 @@ private:
void highlightBreakpoints();
/**
* @brief Finds the earliest offset and breakpoints within the specified range [startPos, endPos]
* in the specified RAnnotatedCode
* in the specified RAnnotatedCode.
*
* This function is supposed to be used for finding the earliest offset and breakpoints within the specified range
* [startPos, endPos]. This will set the value of the variables 'RVA firstOffsetInLine' and 'QVector<RVA> availableBreakpoints' in
* this->mCtxMenu.
* the context menu.
*
* @param codeDecompiled - A reference to the RAnnotatedCode for the function that is decompiled.
* @param startPos - Position of the start of the range(inclusive).
* @param endPos - Position of the end of the range(inclusive).
*/
void gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size_t startPos, size_t endPos);
/**
* @brief Finds the offset that's closest to the specified position in the decompiled code.
*
* @note If no annotations that covers the specified position is found, the first offset in the line
* containing specified position will be returned
*
* @param pos - Position of the decompiled code.
* @return Offset for the specified position/first offset in line.
*/
ut64 offsetForPosition(size_t pos);
/**
* @brief Find the start position of the annotation with the offset that's closest to
* the specified offset
*
* @param offset
* @return Position found or SIZE_MAX
*/
size_t positionForOffset(ut64 offset);
/**
* @brief Updates the view when breakpoints are changed
*/
void updateBreakpoints();
/**
* @brief Set information about the breakpoints on the line in the context menu
*/
void setInfoForBreakpoints();
/**
* @brief Find the context-related annotation covering the specified position.
* If found, set the variable annotationHere in the decompiler context menu.
*
* @param pos Position of cursor in the decompiled code.
*/
void setAnnotationsAtCursor(size_t pos);
};