mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/GP-4007_ghintern_codecompare_actions--SQUASHED'
This commit is contained in:
commit
1b74b8eb9b
17 changed files with 1925 additions and 172 deletions
|
@ -627,7 +627,41 @@
|
||||||
namespace and full signature, including any complex data types and referenced types. Use
|
namespace and full signature, including any complex data types and referenced types. Use
|
||||||
this action with caution as it can potentially add many inappropriate and conflicting
|
this action with caution as it can potentially add many inappropriate and conflicting
|
||||||
data types, especially if the source function is from a program with a different
|
data types, especially if the source function is from a program with a different
|
||||||
architecture or compiler.
|
architecture or compiler. </LI>
|
||||||
|
<A name="Function_Comparison_Apply_Local_Variable_Name">
|
||||||
|
<LI><B>Apply Local Variable Name</B> - Available when the selected and matched
|
||||||
|
decompiler tokens are local variables. Applies just the variable name from the other
|
||||||
|
function. </LI>
|
||||||
|
<A name="Function_Comparison_Apply_Global_Variable_Name">
|
||||||
|
<LI><B>Apply Global Variable Name</B> - Avaiable when the selected and matched
|
||||||
|
decompiler tokens are global variables. Applies just the variable name from the other
|
||||||
|
function. </LI>
|
||||||
|
<A name="Function_Comparison_Apply_Variable_Skeleton_Type">
|
||||||
|
<LI><B>Apply Variable Skeleton Type</B> - Available when the selected and matched
|
||||||
|
decompiler tokens are variables. Applies a limited form of the variable data type to
|
||||||
|
the other function where structures and unions are not copied, but instead empty
|
||||||
|
placeholders are created. </LI>
|
||||||
|
<A name="Function_Comparison_Apply_Variable_Type">
|
||||||
|
<LI><B>Apply Variable Type</B> - Available when the selected and matched decompiler
|
||||||
|
tokens are variables. Applies the full variable type from the other function. Use
|
||||||
|
this action with caution as it can potentially add an inappropriate and conflicting
|
||||||
|
data type, especially if the source function is from a program with a different
|
||||||
|
architecture or compiler. </LI>
|
||||||
|
<A name="Function_Comparison_Apply_Callee_Name">
|
||||||
|
<LI><B>Apply Callee Function Name</B> - Available when the selected and matched
|
||||||
|
decompiler tokens are function calls. Performs the same action as
|
||||||
|
<A href="#Function_Comparison_Apply_Name">Apply Function Name</A>, but acts on the
|
||||||
|
callee functions. </LI>
|
||||||
|
<A name="Function_Comparison_Apply_Callee_Signature">
|
||||||
|
<LI><B>Apply Callee Function Signature</B> - Available when the selected and matched
|
||||||
|
decompiler tokens are function calls. Performs the same action as
|
||||||
|
<A href="#Function_Comparison_Apply_Signature">Apply Function Signature</A>, but acts on
|
||||||
|
the callee functions. </LI>
|
||||||
|
<A name="Function_Comparison_Apply_Callee_Signature_And_Datatypes">
|
||||||
|
<LI><B>Apply Callee Function Signature and Data Types</B> - Available when the selected
|
||||||
|
and matched decompiler tokens are function calls. Performs the same action as
|
||||||
|
<A href="#Function_Comparison_Apply_Signature_And_Datatypes">Apply Function Signature
|
||||||
|
and Data Types</A>, but acts on the callee functions. </LI>
|
||||||
</UL>
|
</UL>
|
||||||
|
|
||||||
<P class="providedbyplugin">Provided By: <I>FunctionComparisonPlugin</I></P>
|
<P class="providedbyplugin">Provided By: <I>FunctionComparisonPlugin</I></P>
|
||||||
|
|
|
@ -27,8 +27,8 @@ import ghidra.util.Msg;
|
||||||
* comparison window
|
* comparison window
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractFunctionComparisonApplyAction extends DockingAction {
|
public abstract class AbstractFunctionComparisonApplyAction extends DockingAction {
|
||||||
protected static final String MENU_PARENT = "Apply From Other";
|
protected static final String MENU_PARENT = "Apply From Other Function";
|
||||||
protected static final String MENU_GROUP = "A0_Apply";
|
protected static final String MENU_GROUP = "A0_ApplyFunction";
|
||||||
protected static final String HELP_TOPIC = "FunctionComparison";
|
protected static final String HELP_TOPIC = "FunctionComparison";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import static ghidra.util.datastruct.Duo.Side.*;
|
||||||
|
|
||||||
|
import ghidra.app.decompiler.ClangFuncNameToken;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclass of {@link AbstractMatchedTokensAction} for actions in a
|
||||||
|
* {@link DecompilerCodeComparisonPanel} that are available only when the matched tokens are
|
||||||
|
* function calls
|
||||||
|
*/
|
||||||
|
public abstract class AbstractMatchedCalleeTokensAction extends AbstractMatchedTokensAction {
|
||||||
|
protected static final String MENU_GROUP = "A2_ApplyCallee";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param actionName name of action
|
||||||
|
* @param owner owner of action
|
||||||
|
* @param diffPanel diff panel containing action
|
||||||
|
* @param disableOnReadOnly if true, action will be disabled for read-only programs
|
||||||
|
*/
|
||||||
|
public AbstractMatchedCalleeTokensAction(String actionName, String owner,
|
||||||
|
DecompilerCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
|
||||||
|
super(actionName, owner, diffPanel, disableOnReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDualDecompilerContext(DualDecompilerActionContext context) {
|
||||||
|
TokenPair tokenPair = context.getTokenPair();
|
||||||
|
|
||||||
|
if (tokenPair == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (tokenPair.leftToken() == null || tokenPair.rightToken() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PcodeOp leftOp = tokenPair.leftToken().getPcodeOp();
|
||||||
|
PcodeOp rightOp = tokenPair.rightToken().getPcodeOp();
|
||||||
|
if (leftOp == null || rightOp == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (leftOp.getOpcode() != PcodeOp.CALL || rightOp.getOpcode() != PcodeOp.CALL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (tokenPair.leftToken() instanceof ClangFuncNameToken) &&
|
||||||
|
(tokenPair.rightToken() instanceof ClangFuncNameToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||||
|
DecompilerCodeComparisonPanel decompPanel = context.getCodeComparisonPanel();
|
||||||
|
|
||||||
|
TokenPair currentPair = context.getTokenPair();
|
||||||
|
|
||||||
|
ClangFuncNameToken leftFuncToken = (ClangFuncNameToken) currentPair.leftToken();
|
||||||
|
ClangFuncNameToken rightFuncToken = (ClangFuncNameToken) currentPair.rightToken();
|
||||||
|
|
||||||
|
Function leftFunction = getFuncFromToken(leftFuncToken, decompPanel.getProgram(LEFT));
|
||||||
|
Function rightFunction = getFuncFromToken(rightFuncToken, decompPanel.getProgram(RIGHT));
|
||||||
|
if (leftFunction == null || rightFunction == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
doCalleeActionPerformed(leftFunction, rightFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once function objects have been recovered from the callee tokens, perform an action
|
||||||
|
* @param leftFunction the callee function on the left side of the decompiler diff panel
|
||||||
|
* @param rightFunction the callee function on the right side of the decompiler diff panel
|
||||||
|
*/
|
||||||
|
protected abstract void doCalleeActionPerformed(Function leftFunction, Function rightFunction);
|
||||||
|
|
||||||
|
private Function getFuncFromToken(ClangFuncNameToken funcToken, Program program) {
|
||||||
|
Address callTarget = funcToken.getPcodeOp().getInput(0).getAddress();
|
||||||
|
Function func = program.getFunctionManager().getFunctionAt(callTarget);
|
||||||
|
if (func == null) {
|
||||||
|
Msg.showWarn(this, null, "Unable to Compare Callees",
|
||||||
|
"Can't compare callees - null Function for " + funcToken.getText());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (func.isExternal()) {
|
||||||
|
Msg.showWarn(this, null, "Unable to Compare Callees",
|
||||||
|
"Can't compare callees - " + func.getName() + " is external");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!func.isThunk()) {
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
func = func.getThunkedFunction(true);
|
||||||
|
if (func.isExternal()) {
|
||||||
|
Msg.showWarn(this, null, "Unable to Compare",
|
||||||
|
"Can't compare callees - " + func.getName() + " is external");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return func;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,24 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.features.codecompare.decompile;
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
import static ghidra.util.datastruct.Duo.Side.*;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
import ghidra.app.decompiler.ClangToken;
|
|
||||||
import ghidra.app.decompiler.DecompilerLocation;
|
|
||||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
|
||||||
import ghidra.features.codecompare.graphanalysis.TokenBin;
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.util.datastruct.Duo.Side;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a base class for actions in a {@link DecompilerCodeComparisonPanel}
|
* This is a base class for actions in a {@link DecompilerCodeComparisonPanel}
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractMatchedTokensAction extends DockingAction {
|
public abstract class AbstractMatchedTokensAction extends DockingAction {
|
||||||
|
protected static final String MENU_PARENT = "Apply From Other Function";
|
||||||
|
protected static final String HELP_TOPIC = "FunctionComparison";
|
||||||
|
|
||||||
protected DecompilerCodeComparisonPanel diffPanel;
|
protected DecompilerCodeComparisonPanel diffPanel;
|
||||||
protected boolean disableOnReadOnly;
|
protected boolean disableOnReadOnly;
|
||||||
|
@ -52,83 +43,43 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
|
||||||
this.disableOnReadOnly = disableOnReadOnly;
|
this.disableOnReadOnly = disableOnReadOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Determines whether the action should be enable for a pair of
|
public void actionPerformed(ActionContext context) {
|
||||||
* matching tokens.
|
if (!(context instanceof DualDecompilerActionContext compareContext)) {
|
||||||
*
|
return;
|
||||||
* @param tokenPair tokens
|
}
|
||||||
* @return true if action should be enabled
|
|
||||||
*/
|
dualDecompilerActionPerformed(compareContext);
|
||||||
protected abstract boolean enabledForTokens(TokenPair tokenPair);
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
if (!(context instanceof DualDecompilerActionContext compareContext)) {
|
if (!(context instanceof DualDecompilerActionContext compareContext)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DecompilerCodeComparisonPanel decompPanel = compareContext.getCodeComparisonPanel();
|
|
||||||
|
|
||||||
if (disableOnReadOnly) {
|
if (disableOnReadOnly) {
|
||||||
//get the program corresponding to the panel with focus
|
if (compareContext.isActiveProgramReadOnly()) {
|
||||||
Side focusedSide = decompPanel.getActiveSide();
|
|
||||||
Program program = decompPanel.getProgram(focusedSide);
|
|
||||||
if (program == null) {
|
|
||||||
return false; //panel initializing; don't enable action
|
|
||||||
}
|
|
||||||
if (!program.canSave()) {
|
|
||||||
return false; // program is read-only, don't enable action
|
return false; // program is read-only, don't enable action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenPair currentPair = getCurrentTokenPair(decompPanel);
|
return isEnabledForDualDecompilerContext(compareContext);
|
||||||
return enabledForTokens(currentPair);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link TokenPair} consisting of the token under the cursor in the focused
|
* Subclasses return true if they are enabled for the given context
|
||||||
* decompiler panel and its counterpart in the other panel.
|
|
||||||
*
|
*
|
||||||
* @param decompPanel decomp panel
|
* @param context the context
|
||||||
* @return matching tokens (or null if no match)
|
* @return true if enabled
|
||||||
*/
|
*/
|
||||||
protected TokenPair getCurrentTokenPair(
|
protected abstract boolean isEnabledForDualDecompilerContext(
|
||||||
DecompilerCodeComparisonPanel decompPanel) {
|
DualDecompilerActionContext context);
|
||||||
|
|
||||||
DecompilerPanel focusedPanel = decompPanel.getActiveDisplay().getDecompilerPanel();
|
/**
|
||||||
|
* Subclasses will perform their work in this method
|
||||||
if (!(focusedPanel.getCurrentLocation() instanceof DecompilerLocation focusedLocation)) {
|
* @param context the context
|
||||||
return null;
|
*/
|
||||||
}
|
protected abstract void dualDecompilerActionPerformed(DualDecompilerActionContext context);
|
||||||
|
|
||||||
ClangToken focusedToken = focusedLocation.getToken();
|
|
||||||
if (focusedToken == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
List<TokenBin> tokenBin = diffPanel.getHighBins();
|
|
||||||
if (tokenBin == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
TokenBin containingBin = TokenBin.getBinContainingToken(tokenBin, focusedToken);
|
|
||||||
if (containingBin == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
TokenBin matchedBin = containingBin.getMatch();
|
|
||||||
if (matchedBin == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
//loop over the tokens in the matching bin and return the first one in the same
|
|
||||||
//class as focusedToken
|
|
||||||
Iterator<ClangToken> tokenIter = matchedBin.iterator();
|
|
||||||
while (tokenIter.hasNext()) {
|
|
||||||
ClangToken currentMatch = tokenIter.next();
|
|
||||||
if (currentMatch.getClass().equals(focusedToken.getClass())) {
|
|
||||||
return decompPanel.getActiveSide() == LEFT
|
|
||||||
? new TokenPair(focusedToken, currentMatch)
|
|
||||||
: new TokenPair(currentMatch, focusedToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.util.FunctionUtility;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.datastruct.Duo.Side;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action for transferring the 'skeleton' function signature between matched callee tokens.
|
||||||
|
*/
|
||||||
|
public class ApplyCalleeEmptySignatureFromMatchedTokensAction
|
||||||
|
extends AbstractMatchedCalleeTokensAction {
|
||||||
|
|
||||||
|
private PluginTool tool;
|
||||||
|
public static final String ACTION_NAME = "Function Comparison Apply Callee Signature";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor
|
||||||
|
* @param diffPanel diff panel
|
||||||
|
* @param tool tool
|
||||||
|
*/
|
||||||
|
public ApplyCalleeEmptySignatureFromMatchedTokensAction(
|
||||||
|
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
|
||||||
|
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
|
MenuData menuData =
|
||||||
|
new MenuData(new String[] { MENU_PARENT, "Callee Signature" }, null,
|
||||||
|
MENU_GROUP);
|
||||||
|
setPopupMenuData(menuData);
|
||||||
|
setHelpLocation(new HelpLocation(HELP_TOPIC, getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||||
|
|
||||||
|
Side activeSide = diffPanel.getActiveSide();
|
||||||
|
|
||||||
|
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
|
||||||
|
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;
|
||||||
|
|
||||||
|
Program activeProgram = activeFunction.getProgram();
|
||||||
|
|
||||||
|
try {
|
||||||
|
activeProgram.withTransaction("Code Comparison Transfer Callee Signature",
|
||||||
|
() -> FunctionUtility.applySignature(activeFunction, otherFunction, true, null));
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Msg.showError(this, tool.getToolFrame(), "Failed to Apply Callee Signature",
|
||||||
|
e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.util.FunctionUtility;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.datastruct.Duo.Side;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action for transferring just the function name and namespace between matched callee tokens.
|
||||||
|
*/
|
||||||
|
public class ApplyCalleeFunctionNameFromMatchedTokensAction
|
||||||
|
extends AbstractMatchedCalleeTokensAction {
|
||||||
|
|
||||||
|
private PluginTool tool;
|
||||||
|
public static final String ACTION_NAME = "Function Comparison Apply Callee Name";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor
|
||||||
|
* @param diffPanel diff panel
|
||||||
|
* @param tool tool
|
||||||
|
*/
|
||||||
|
public ApplyCalleeFunctionNameFromMatchedTokensAction(
|
||||||
|
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
|
||||||
|
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
|
MenuData menuData =
|
||||||
|
new MenuData(new String[] { MENU_PARENT, "Callee Name" }, null, MENU_GROUP);
|
||||||
|
setPopupMenuData(menuData);
|
||||||
|
setHelpLocation(new HelpLocation(HELP_TOPIC, getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||||
|
|
||||||
|
Side activeSide = diffPanel.getActiveSide();
|
||||||
|
|
||||||
|
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
|
||||||
|
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;
|
||||||
|
|
||||||
|
Program activeProgram = activeFunction.getProgram();
|
||||||
|
|
||||||
|
try {
|
||||||
|
activeProgram.withTransaction("Code Comparison Transfer Callee Function Name",
|
||||||
|
() -> FunctionUtility.applyNameAndNamespace(activeFunction, otherFunction));
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Msg.showError(this, tool.getToolFrame(), "Failed to Apply Callee Function Name",
|
||||||
|
e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.util.FunctionUtility;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.datastruct.Duo.Side;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action for transferring the full function signature between matched callee tokens
|
||||||
|
*/
|
||||||
|
public class ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction
|
||||||
|
extends AbstractMatchedCalleeTokensAction {
|
||||||
|
private PluginTool tool;
|
||||||
|
public static final String ACTION_NAME =
|
||||||
|
"Function Comparison Apply Callee Signature And Datatypes";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor
|
||||||
|
* @param diffPanel diff panel
|
||||||
|
* @param tool tool
|
||||||
|
*/
|
||||||
|
public ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction(
|
||||||
|
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
|
||||||
|
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
|
MenuData menuData =
|
||||||
|
new MenuData(new String[] { MENU_PARENT, "Callee Signature and Data Types" },
|
||||||
|
null, MENU_GROUP);
|
||||||
|
setPopupMenuData(menuData);
|
||||||
|
setHelpLocation(new HelpLocation(HELP_TOPIC, getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||||
|
|
||||||
|
Side activeSide = diffPanel.getActiveSide();
|
||||||
|
|
||||||
|
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
|
||||||
|
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;
|
||||||
|
|
||||||
|
Program activeProgram = activeFunction.getProgram();
|
||||||
|
|
||||||
|
try {
|
||||||
|
activeProgram.withTransaction(
|
||||||
|
"Code Comparison Transfer Callee Signature and Data Types",
|
||||||
|
() -> FunctionUtility.applySignature(activeFunction, otherFunction, false, null));
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Msg.showError(this, tool.getToolFrame(),
|
||||||
|
"Failed to Apply Callee Signature and Data Types", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.decompiler.ClangVariableToken;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.data.DataType;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||||
|
import ghidra.program.model.pcode.HighSymbol;
|
||||||
|
import ghidra.program.model.symbol.SourceType;
|
||||||
|
import ghidra.program.util.DataTypeCleaner;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.datastruct.Duo.Side;
|
||||||
|
import ghidra.util.exception.*;
|
||||||
|
|
||||||
|
public class ApplyEmptyVariableTypeFromMatchedTokensAction extends AbstractMatchedTokensAction {
|
||||||
|
private PluginTool tool;
|
||||||
|
public static final String ACTION_NAME = "Function Comparison Apply Variable Skeleton Type";
|
||||||
|
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||||
|
|
||||||
|
public ApplyEmptyVariableTypeFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||||
|
PluginTool tool) {
|
||||||
|
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
|
MenuData menuData =
|
||||||
|
new MenuData(new String[] { MENU_PARENT, "Variable Skeleton Type" }, null, MENU_GROUP);
|
||||||
|
setPopupMenuData(menuData);
|
||||||
|
setHelpLocation(new HelpLocation(HELP_TOPIC, getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDualDecompilerContext(DualDecompilerActionContext context) {
|
||||||
|
TokenPair tokenPair = context.getTokenPair();
|
||||||
|
|
||||||
|
if (tokenPair == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenPair.leftToken() == null || tokenPair.rightToken() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(tokenPair.leftToken() instanceof ClangVariableToken leftVar) ||
|
||||||
|
!(tokenPair.rightToken() instanceof ClangVariableToken rightVar)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HighSymbol leftSymbol = leftVar.getHighSymbol(context.getHighFunction(Side.LEFT));
|
||||||
|
HighSymbol rightSymbol = rightVar.getHighSymbol(context.getHighFunction(Side.RIGHT));
|
||||||
|
|
||||||
|
return leftSymbol != null && rightSymbol != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||||
|
TokenPair currentPair = context.getTokenPair();
|
||||||
|
|
||||||
|
Side activeSide = diffPanel.getActiveSide();
|
||||||
|
|
||||||
|
ClangVariableToken activeToken =
|
||||||
|
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||||
|
: (ClangVariableToken) currentPair.rightToken();
|
||||||
|
ClangVariableToken otherToken =
|
||||||
|
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.rightToken()
|
||||||
|
: (ClangVariableToken) currentPair.leftToken();
|
||||||
|
|
||||||
|
HighSymbol activeHighSymbol =
|
||||||
|
activeToken.getHighSymbol(context.getHighFunction(activeSide));
|
||||||
|
HighSymbol otherHighSymbol =
|
||||||
|
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||||
|
|
||||||
|
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
|
||||||
|
Program activeProgram = activeFunction.getProgram();
|
||||||
|
|
||||||
|
DataType dt = otherHighSymbol.getDataType();
|
||||||
|
|
||||||
|
try {
|
||||||
|
activeProgram.withTransaction("Code Comparison Transfer Local Skeleton Type", () -> {
|
||||||
|
DataTypeCleaner dtCleaner =
|
||||||
|
new DataTypeCleaner(activeProgram.getDataTypeManager(), true);
|
||||||
|
try {
|
||||||
|
DataType resolvedDt = dtCleaner.clean(dt);
|
||||||
|
resolvedDt = activeProgram.getDataTypeManager().resolve(resolvedDt, null);
|
||||||
|
|
||||||
|
HighFunctionDBUtil.updateDBVariable(activeHighSymbol, null, resolvedDt,
|
||||||
|
SourceType.IMPORTED);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
dtCleaner.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (InvalidInputException e) {
|
||||||
|
Msg.showError(this, tool.getToolFrame(), "Skeleton Type Transfer Failed",
|
||||||
|
"Could not bring cleaned type " + dt.getName() + " to " + activeProgram.getName());
|
||||||
|
}
|
||||||
|
catch (UsrException e) {
|
||||||
|
throw new AssertException("Unexpected exception", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.cmd.label.RenameLabelCmd;
|
||||||
|
import ghidra.app.decompiler.ClangVariableToken;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.pcode.HighCodeSymbol;
|
||||||
|
import ghidra.program.model.pcode.HighSymbol;
|
||||||
|
import ghidra.program.model.symbol.*;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.datastruct.Duo.Side;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action for transferring the name of a global variable between matched tokens.
|
||||||
|
*/
|
||||||
|
public class ApplyGlobalNameFromMatchedTokensAction extends AbstractMatchedTokensAction {
|
||||||
|
private PluginTool tool;
|
||||||
|
public static final String ACTION_NAME = "Function Comparison Apply Global Variable Name";
|
||||||
|
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor
|
||||||
|
* @param diffPanel diff panel
|
||||||
|
* @param tool tool
|
||||||
|
*/
|
||||||
|
public ApplyGlobalNameFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||||
|
PluginTool tool) {
|
||||||
|
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
|
MenuData menuData =
|
||||||
|
new MenuData(new String[] { MENU_PARENT, "Variable Name" }, null, MENU_GROUP);
|
||||||
|
setPopupMenuData(menuData);
|
||||||
|
setHelpLocation(new HelpLocation(HELP_TOPIC, getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDualDecompilerContext(DualDecompilerActionContext context) {
|
||||||
|
TokenPair tokenPair = context.getTokenPair();
|
||||||
|
|
||||||
|
if (tokenPair == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenPair.leftToken() == null || tokenPair.rightToken() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(tokenPair.leftToken() instanceof ClangVariableToken leftVar) ||
|
||||||
|
!(tokenPair.rightToken() instanceof ClangVariableToken rightVar)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HighSymbol leftSymbol = leftVar.getHighSymbol(context.getHighFunction(Side.LEFT));
|
||||||
|
HighSymbol rightSymbol = rightVar.getHighSymbol(context.getHighFunction(Side.RIGHT));
|
||||||
|
|
||||||
|
if (leftSymbol == null || rightSymbol == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return leftSymbol.isGlobal() && rightSymbol.isGlobal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||||
|
TokenPair currentPair = context.getTokenPair();
|
||||||
|
|
||||||
|
Side activeSide = diffPanel.getActiveSide();
|
||||||
|
ClangVariableToken activeToken =
|
||||||
|
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||||
|
: (ClangVariableToken) currentPair.rightToken();
|
||||||
|
ClangVariableToken otherToken =
|
||||||
|
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.rightToken()
|
||||||
|
: (ClangVariableToken) currentPair.leftToken();
|
||||||
|
|
||||||
|
HighSymbol activeHighSymbol =
|
||||||
|
activeToken.getHighSymbol(context.getHighFunction(activeSide));
|
||||||
|
HighSymbol otherHighSymbol =
|
||||||
|
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||||
|
|
||||||
|
Program activeProgram = context.getCodeComparisonPanel().getProgram(activeSide);
|
||||||
|
|
||||||
|
Symbol activeSymbol = null;
|
||||||
|
if (activeHighSymbol instanceof HighCodeSymbol activeCodeSymbol) {
|
||||||
|
activeSymbol = activeCodeSymbol.getCodeSymbol();
|
||||||
|
if (activeSymbol == null) {
|
||||||
|
Address addr = activeCodeSymbol.getStorage().getMinAddress();
|
||||||
|
SymbolTable symbolTable = activeProgram.getSymbolTable();
|
||||||
|
activeSymbol = symbolTable.getPrimarySymbol(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (activeSymbol == null) {
|
||||||
|
Msg.showError(this, tool.getToolFrame(), "Name transfer failed",
|
||||||
|
"Failed to find memory storage for target global");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenameLabelCmd cmd = new RenameLabelCmd(activeSymbol, otherHighSymbol.getName(),
|
||||||
|
otherHighSymbol.getNamespace(), SourceType.IMPORTED);
|
||||||
|
|
||||||
|
activeProgram.withTransaction("Code Comparison Apply Global Variable Name",
|
||||||
|
() -> cmd.applyTo(activeProgram));
|
||||||
|
|
||||||
|
if (!cmd.getStatusMsg().isEmpty()) {
|
||||||
|
Msg.showError(this, tool.getToolFrame(), "Name transfer failed", cmd.getStatusMsg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.decompiler.ClangVariableToken;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||||
|
import ghidra.program.model.pcode.HighSymbol;
|
||||||
|
import ghidra.program.model.symbol.SourceType;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.datastruct.Duo.Side;
|
||||||
|
import ghidra.util.exception.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action for transferring the name of a local variable between matched tokens.
|
||||||
|
*/
|
||||||
|
public class ApplyLocalNameFromMatchedTokensAction extends AbstractMatchedTokensAction {
|
||||||
|
private PluginTool tool;
|
||||||
|
public static final String ACTION_NAME = "Function Comparison Apply Local Variable Name";
|
||||||
|
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor
|
||||||
|
* @param diffPanel diff panel
|
||||||
|
* @param tool tool
|
||||||
|
*/
|
||||||
|
public ApplyLocalNameFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||||
|
PluginTool tool) {
|
||||||
|
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
|
MenuData menuData =
|
||||||
|
new MenuData(new String[] { MENU_PARENT, "Variable Name" }, null, MENU_GROUP);
|
||||||
|
setPopupMenuData(menuData);
|
||||||
|
setHelpLocation(new HelpLocation(HELP_TOPIC, getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDualDecompilerContext(DualDecompilerActionContext context) {
|
||||||
|
TokenPair tokenPair = context.getTokenPair();
|
||||||
|
|
||||||
|
if (tokenPair == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenPair.leftToken() == null || tokenPair.rightToken() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(tokenPair.leftToken() instanceof ClangVariableToken leftVar) ||
|
||||||
|
!(tokenPair.rightToken() instanceof ClangVariableToken rightVar)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HighSymbol leftSymbol = leftVar.getHighSymbol(context.getHighFunction(Side.LEFT));
|
||||||
|
HighSymbol rightSymbol = rightVar.getHighSymbol(context.getHighFunction(Side.RIGHT));
|
||||||
|
|
||||||
|
if (leftSymbol == null || rightSymbol == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !leftSymbol.isGlobal() && !rightSymbol.isGlobal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||||
|
TokenPair currentPair = context.getTokenPair();
|
||||||
|
|
||||||
|
Side activeSide = diffPanel.getActiveSide();
|
||||||
|
|
||||||
|
ClangVariableToken activeToken =
|
||||||
|
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||||
|
: (ClangVariableToken) currentPair.rightToken();
|
||||||
|
ClangVariableToken otherToken =
|
||||||
|
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.rightToken()
|
||||||
|
: (ClangVariableToken) currentPair.leftToken();
|
||||||
|
|
||||||
|
HighSymbol activeHighSymbol =
|
||||||
|
activeToken.getHighSymbol(context.getHighFunction(activeSide));
|
||||||
|
HighSymbol otherHighSymbol =
|
||||||
|
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||||
|
|
||||||
|
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
|
||||||
|
Program activeProgram = activeFunction.getProgram();
|
||||||
|
|
||||||
|
try {
|
||||||
|
activeProgram.withTransaction("Code Comparison Apply Local Variable Name",
|
||||||
|
() -> HighFunctionDBUtil.updateDBVariable(activeHighSymbol,
|
||||||
|
otherHighSymbol.getName(),
|
||||||
|
null, SourceType.IMPORTED));
|
||||||
|
}
|
||||||
|
catch (DuplicateNameException e) {
|
||||||
|
Msg.showError(this, tool.getToolFrame(), "Duplicate Name",
|
||||||
|
"Name " + otherHighSymbol.getName() + " already exists in function " +
|
||||||
|
activeFunction.getName());
|
||||||
|
}
|
||||||
|
catch (UsrException e) {
|
||||||
|
throw new AssertException("Unexpected exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.decompiler.ClangVariableToken;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.data.DataType;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||||
|
import ghidra.program.model.pcode.HighSymbol;
|
||||||
|
import ghidra.program.model.symbol.SourceType;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.datastruct.Duo.Side;
|
||||||
|
import ghidra.util.exception.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action for transferring the type of a local or global variable between matched tokens.
|
||||||
|
*/
|
||||||
|
public class ApplyVariableTypeFromMatchedTokensAction extends AbstractMatchedTokensAction {
|
||||||
|
private PluginTool tool;
|
||||||
|
public static final String ACTION_NAME = "Function Comparison Apply Variable Type";
|
||||||
|
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor
|
||||||
|
* @param diffPanel diff panel
|
||||||
|
* @param tool tool
|
||||||
|
*/
|
||||||
|
public ApplyVariableTypeFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||||
|
PluginTool tool) {
|
||||||
|
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
|
MenuData menuData =
|
||||||
|
new MenuData(new String[] { MENU_PARENT, "Variable Type" }, null, MENU_GROUP);
|
||||||
|
setPopupMenuData(menuData);
|
||||||
|
setHelpLocation(new HelpLocation(HELP_TOPIC, getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDualDecompilerContext(DualDecompilerActionContext context) {
|
||||||
|
TokenPair tokenPair = context.getTokenPair();
|
||||||
|
|
||||||
|
if (tokenPair == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenPair.leftToken() == null || tokenPair.rightToken() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(tokenPair.leftToken() instanceof ClangVariableToken leftVar) ||
|
||||||
|
!(tokenPair.rightToken() instanceof ClangVariableToken rightVar)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HighSymbol leftSymbol = leftVar.getHighSymbol(context.getHighFunction(Side.LEFT));
|
||||||
|
HighSymbol rightSymbol = rightVar.getHighSymbol(context.getHighFunction(Side.RIGHT));
|
||||||
|
|
||||||
|
return leftSymbol != null && rightSymbol != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||||
|
TokenPair currentPair = context.getTokenPair();
|
||||||
|
|
||||||
|
Side activeSide = diffPanel.getActiveSide();
|
||||||
|
|
||||||
|
ClangVariableToken activeToken =
|
||||||
|
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||||
|
: (ClangVariableToken) currentPair.rightToken();
|
||||||
|
ClangVariableToken otherToken =
|
||||||
|
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.rightToken()
|
||||||
|
: (ClangVariableToken) currentPair.leftToken();
|
||||||
|
|
||||||
|
HighSymbol activeHighSymbol =
|
||||||
|
activeToken.getHighSymbol(context.getHighFunction(activeSide));
|
||||||
|
HighSymbol otherHighSymbol =
|
||||||
|
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||||
|
|
||||||
|
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
|
||||||
|
Program activeProgram = activeFunction.getProgram();
|
||||||
|
|
||||||
|
DataType dt = otherHighSymbol.getDataType();
|
||||||
|
|
||||||
|
try {
|
||||||
|
activeProgram.withTransaction("Code Comparison Transfer Local Type", () -> {
|
||||||
|
DataType resolvedDt = activeProgram.getDataTypeManager().resolve(dt, null);
|
||||||
|
HighFunctionDBUtil.updateDBVariable(activeHighSymbol, null, resolvedDt,
|
||||||
|
SourceType.IMPORTED);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (InvalidInputException e) {
|
||||||
|
Msg.showError(this, tool.getToolFrame(), "Type Transfer Failed",
|
||||||
|
"Could not bring type " + dt.getName() + " to " + activeProgram.getName());
|
||||||
|
}
|
||||||
|
catch (UsrException e) {
|
||||||
|
throw new AssertException("Unexpected exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,17 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.features.codecompare.decompile;
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
import static ghidra.util.datastruct.Duo.Side.*;
|
|
||||||
|
|
||||||
import docking.ActionContext;
|
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
import ghidra.app.decompiler.ClangFuncNameToken;
|
|
||||||
import ghidra.app.services.FunctionComparisonService;
|
import ghidra.app.services.FunctionComparisonService;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
@ -33,11 +26,9 @@ import ghidra.util.Msg;
|
||||||
* An action for bringing up a side-by-side function comparison of callees with matching
|
* An action for bringing up a side-by-side function comparison of callees with matching
|
||||||
* tokens.
|
* tokens.
|
||||||
*/
|
*/
|
||||||
public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAction {
|
public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedCalleeTokensAction {
|
||||||
private PluginTool tool;
|
private PluginTool tool;
|
||||||
private static final String ACTION_NAME = "Compare Matching Callees";
|
private static final String ACTION_NAME = "Compare Matching Callees";
|
||||||
private static final String MENU_GROUP = "A1_Compare";
|
|
||||||
private static final String HELP_TOPIC = "FunctionComparison";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -48,58 +39,14 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAc
|
||||||
PluginTool tool) {
|
PluginTool tool) {
|
||||||
super(ACTION_NAME, tool.getName(), diffPanel, false);
|
super(ACTION_NAME, tool.getName(), diffPanel, false);
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
FunctionComparisonService service = tool.getService(FunctionComparisonService.class);
|
|
||||||
if (service != null) {
|
|
||||||
MenuData menuData = new MenuData(new String[] { ACTION_NAME }, null, MENU_GROUP);
|
MenuData menuData = new MenuData(new String[] { ACTION_NAME }, null, MENU_GROUP);
|
||||||
setPopupMenuData(menuData);
|
setPopupMenuData(menuData);
|
||||||
setEnabled(true);
|
|
||||||
setHelpLocation(new HelpLocation(HELP_TOPIC, "Compare Matching Callees"));
|
setHelpLocation(new HelpLocation(HELP_TOPIC, "Compare Matching Callees"));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean enabledForTokens(TokenPair tokenPair) {
|
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||||
if (tokenPair == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tokenPair.leftToken() == null || tokenPair.rightToken() == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
PcodeOp leftOp = tokenPair.leftToken().getPcodeOp();
|
|
||||||
PcodeOp rightOp = tokenPair.rightToken().getPcodeOp();
|
|
||||||
if (leftOp == null || rightOp == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (leftOp.getOpcode() != PcodeOp.CALL || rightOp.getOpcode() != PcodeOp.CALL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (tokenPair.leftToken() instanceof ClangFuncNameToken) &&
|
|
||||||
(tokenPair.rightToken() instanceof ClangFuncNameToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
if (!(context instanceof DualDecompilerActionContext compareContext)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DecompilerCodeComparisonPanel decompPanel = compareContext.getCodeComparisonPanel();
|
|
||||||
|
|
||||||
TokenPair currentPair = getCurrentTokenPair(decompPanel);
|
|
||||||
if (currentPair == null || currentPair.leftToken() == null ||
|
|
||||||
currentPair.rightToken() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClangFuncNameToken leftFuncToken = (ClangFuncNameToken) currentPair.leftToken();
|
|
||||||
ClangFuncNameToken rightFuncToken = (ClangFuncNameToken) currentPair.rightToken();
|
|
||||||
|
|
||||||
Function leftFunction = getFuncFromToken(leftFuncToken, decompPanel.getProgram(LEFT));
|
|
||||||
Function rightFunction = getFuncFromToken(rightFuncToken, decompPanel.getProgram(RIGHT));
|
|
||||||
if (leftFunction == null || rightFunction == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionComparisonService service = tool.getService(FunctionComparisonService.class);
|
FunctionComparisonService service = tool.getService(FunctionComparisonService.class);
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
Msg.error(this, "Function Comparison Service not found!");
|
Msg.error(this, "Function Comparison Service not found!");
|
||||||
|
@ -108,30 +55,4 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAc
|
||||||
service.createComparison(leftFunction, rightFunction);
|
service.createComparison(leftFunction, rightFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Function getFuncFromToken(ClangFuncNameToken funcToken, Program program) {
|
|
||||||
Address callTarget = funcToken.getPcodeOp().getInput(0).getAddress();
|
|
||||||
Function func = program.getFunctionManager().getFunctionAt(callTarget);
|
|
||||||
if (func == null) {
|
|
||||||
Msg.showWarn(this, null, "Unable to Compare Callees",
|
|
||||||
"Can't compare callees - null Function for " + funcToken.getText());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (func.isExternal()) {
|
|
||||||
Msg.showWarn(this, null, "Unable to Compare Callees",
|
|
||||||
"Can't compare callees - " + func.getName() + " is external");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!func.isThunk()) {
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
func = func.getThunkedFunction(true);
|
|
||||||
if (func.isExternal()) {
|
|
||||||
Msg.showWarn(this, null, "Unable to Compare",
|
|
||||||
"Can't compare callees - " + func.getName() + " is external");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return func;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,6 +182,13 @@ public class DecompilerCodeComparisonPanel
|
||||||
actions.add(new DecompilerCodeComparisonOptionsAction());
|
actions.add(new DecompilerCodeComparisonOptionsAction());
|
||||||
actions.add(toggleExactConstantMatchingAction);
|
actions.add(toggleExactConstantMatchingAction);
|
||||||
actions.add(new CompareFuncsFromMatchedTokensAction(this, tool));
|
actions.add(new CompareFuncsFromMatchedTokensAction(this, tool));
|
||||||
|
actions.add(new ApplyLocalNameFromMatchedTokensAction(this, tool));
|
||||||
|
actions.add(new ApplyGlobalNameFromMatchedTokensAction(this, tool));
|
||||||
|
actions.add(new ApplyVariableTypeFromMatchedTokensAction(this, tool));
|
||||||
|
actions.add(new ApplyEmptyVariableTypeFromMatchedTokensAction(this, tool));
|
||||||
|
actions.add(new ApplyCalleeFunctionNameFromMatchedTokensAction(this, tool));
|
||||||
|
actions.add(new ApplyCalleeEmptySignatureFromMatchedTokensAction(this, tool));
|
||||||
|
actions.add(new ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction(this, tool));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decompileDataSet(Side side, DecompileData dcompileData) {
|
private void decompileDataSet(Side side, DecompileData dcompileData) {
|
||||||
|
|
|
@ -15,11 +15,22 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.features.codecompare.decompile;
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import static ghidra.util.datastruct.Duo.Side.*;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import docking.ComponentProvider;
|
import docking.ComponentProvider;
|
||||||
import ghidra.app.context.RestrictedAddressSetContext;
|
import ghidra.app.context.RestrictedAddressSetContext;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.DecompilerLocation;
|
||||||
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
import ghidra.features.base.codecompare.panel.CodeComparisonActionContext;
|
import ghidra.features.base.codecompare.panel.CodeComparisonActionContext;
|
||||||
|
import ghidra.features.codecompare.graphanalysis.TokenBin;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.pcode.HighFunction;
|
||||||
|
import ghidra.util.datastruct.Duo.Side;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action context for a dual decompiler panel.
|
* Action context for a dual decompiler panel.
|
||||||
|
@ -28,6 +39,8 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
||||||
implements RestrictedAddressSetContext {
|
implements RestrictedAddressSetContext {
|
||||||
|
|
||||||
private DecompilerCodeComparisonPanel decompilerComparisonPanel = null;
|
private DecompilerCodeComparisonPanel decompilerComparisonPanel = null;
|
||||||
|
private TokenPair tokenPair;
|
||||||
|
private boolean overrideReadOnly = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an action context for a dual decompiler panel.
|
* Creates an action context for a dual decompiler panel.
|
||||||
|
@ -38,7 +51,47 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
||||||
public DualDecompilerActionContext(ComponentProvider provider,
|
public DualDecompilerActionContext(ComponentProvider provider,
|
||||||
DecompilerCodeComparisonPanel panel, Component source) {
|
DecompilerCodeComparisonPanel panel, Component source) {
|
||||||
super(provider, panel, source);
|
super(provider, panel, source);
|
||||||
this.decompilerComparisonPanel = panel;
|
decompilerComparisonPanel = panel;
|
||||||
|
tokenPair = computeTokenPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TokenPair computeTokenPair() {
|
||||||
|
DecompilerPanel focusedPanel =
|
||||||
|
decompilerComparisonPanel.getActiveDisplay().getDecompilerPanel();
|
||||||
|
|
||||||
|
if (!(focusedPanel.getCurrentLocation() instanceof DecompilerLocation focusedLocation)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClangToken focusedToken = focusedLocation.getToken();
|
||||||
|
if (focusedToken == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<TokenBin> tokenBin = decompilerComparisonPanel.getHighBins();
|
||||||
|
if (tokenBin == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
TokenBin containingBin = TokenBin.getBinContainingToken(tokenBin, focusedToken);
|
||||||
|
if (containingBin == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
TokenBin matchedBin = containingBin.getMatch();
|
||||||
|
if (matchedBin == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//loop over the tokens in the matching bin and return the first one in the same
|
||||||
|
//class as focusedToken
|
||||||
|
Iterator<ClangToken> tokenIter = matchedBin.iterator();
|
||||||
|
while (tokenIter.hasNext()) {
|
||||||
|
ClangToken currentMatch = tokenIter.next();
|
||||||
|
if (currentMatch.getClass().equals(focusedToken.getClass())) {
|
||||||
|
return decompilerComparisonPanel.getActiveSide() == LEFT
|
||||||
|
? new TokenPair(focusedToken, currentMatch)
|
||||||
|
: new TokenPair(currentMatch, focusedToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,4 +102,55 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
||||||
public DecompilerCodeComparisonPanel getCodeComparisonPanel() {
|
public DecompilerCodeComparisonPanel getCodeComparisonPanel() {
|
||||||
return decompilerComparisonPanel;
|
return decompilerComparisonPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link HighFunction} being viewed on the given side by the decompiler panel that
|
||||||
|
* generated this context
|
||||||
|
* @param side the side of the comparison to retrieve the high function for
|
||||||
|
* @return the high function on the given side of the comparison panel that generated this
|
||||||
|
* context
|
||||||
|
*/
|
||||||
|
public HighFunction getHighFunction(Side side) {
|
||||||
|
return decompilerComparisonPanel.getDecompilerPanel(side).getController().getHighFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link TokenPair} currently selected in the diff view, if any.
|
||||||
|
* @return the token pair selected when this context was generated
|
||||||
|
*/
|
||||||
|
public TokenPair getTokenPair() {
|
||||||
|
return tokenPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether this context will bypass a check to the actual state of the active program
|
||||||
|
* when resolving {@link #isActiveProgramReadOnly}. Used by tests.
|
||||||
|
* @param overrideReadOnly true if this context should bypass an
|
||||||
|
* {@link #isActiveProgramReadOnly} by always returning false
|
||||||
|
*/
|
||||||
|
void setOverrideReadOnly(boolean overrideReadOnly) {
|
||||||
|
this.overrideReadOnly = overrideReadOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the program associated with the focused window in the dual decompiler view is
|
||||||
|
* read only. Always false if read only override was set to true with a call to
|
||||||
|
* {@link #setOverrideReadOnly}
|
||||||
|
* @return true if the active program is read only, always false if override is set to true
|
||||||
|
*/
|
||||||
|
public boolean isActiveProgramReadOnly() {
|
||||||
|
if (overrideReadOnly) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program activeProgram =
|
||||||
|
decompilerComparisonPanel.getProgram(decompilerComparisonPanel.getActiveSide());
|
||||||
|
|
||||||
|
if (activeProgram == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeProgram.getDomainFile().isReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import docking.widgets.fieldpanel.FieldPanel;
|
||||||
|
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.component.ClangTextField;
|
||||||
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
|
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||||
|
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||||
|
import ghidra.features.codecompare.plugin.FunctionComparisonPlugin;
|
||||||
|
import ghidra.features.codecompare.plugin.FunctionComparisonProvider;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
|
import ghidra.test.TestEnv;
|
||||||
|
import ghidra.util.datastruct.Duo.Side;
|
||||||
|
|
||||||
|
public abstract class AbstractDualDecompilerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
protected TestEnv env;
|
||||||
|
protected FunctionComparisonPlugin fcPlugin;
|
||||||
|
protected FunctionPlugin fPlugin;
|
||||||
|
protected CodeBrowserPlugin cbPlugin;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
env = new TestEnv();
|
||||||
|
fcPlugin = env.addPlugin(FunctionComparisonPlugin.class);
|
||||||
|
fPlugin = env.addPlugin(FunctionPlugin.class);
|
||||||
|
cbPlugin = env.addPlugin(CodeBrowserPlugin.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
env.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FunctionComparisonProvider compareFunctions(Set<Function> functions) {
|
||||||
|
runSwing(() -> fcPlugin.createComparison(functions));
|
||||||
|
waitForSwing();
|
||||||
|
return waitForComponentProvider(FunctionComparisonProvider.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DecompilerCodeComparisonPanel findDecompilerPanel(
|
||||||
|
FunctionComparisonProvider provider) {
|
||||||
|
for (CodeComparisonPanel panel : provider.getComponent().getComparisonPanels()) {
|
||||||
|
if (panel instanceof DecompilerCodeComparisonPanel decompPanel) {
|
||||||
|
return decompPanel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setActivePanel(FunctionComparisonProvider provider, CodeComparisonPanel panel) {
|
||||||
|
runSwing(() -> provider.getComponent().setCurrentTabbedComponent(panel.getName()));
|
||||||
|
waitForSwing();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void waitForDecompile(DecompilerCodeComparisonPanel panel) {
|
||||||
|
waitForSwing();
|
||||||
|
waitForCondition(() -> !panel.isBusy());
|
||||||
|
waitForSwing();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DecompilerPanel getDecompSide(DecompilerCodeComparisonPanel panel, Side side) {
|
||||||
|
CDisplay sideDisplay = side == Side.LEFT ? panel.getLeftPanel() : panel.getRightPanel();
|
||||||
|
return sideDisplay.getDecompilerPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1-indexed lines
|
||||||
|
protected ClangToken setDecompLocation(DecompilerCodeComparisonPanel comparePanel, Side side,
|
||||||
|
int line, int charPos) {
|
||||||
|
DecompilerPanel panel = getDecompSide(comparePanel, side);
|
||||||
|
FieldPanel fp = panel.getFieldPanel();
|
||||||
|
FieldLocation loc = new FieldLocation(line - 1, 0, 0, charPos); // 0-indexed lines
|
||||||
|
|
||||||
|
fp.scrollTo(loc);
|
||||||
|
|
||||||
|
Point p = fp.getPointForLocation(loc);
|
||||||
|
|
||||||
|
click(fp, p, 1, true);
|
||||||
|
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
return getCurrentToken(comparePanel, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the token under the cursor at the given side
|
||||||
|
protected ClangToken getCurrentToken(DecompilerCodeComparisonPanel comparePanel, Side side) {
|
||||||
|
DecompilerPanel panel = getDecompSide(comparePanel, side);
|
||||||
|
FieldLocation loc = panel.getCursorPosition();
|
||||||
|
int lineNumber = loc.getIndex().intValue();
|
||||||
|
ClangTextField field = (ClangTextField) panel.getFields().get(lineNumber);
|
||||||
|
return field.getToken(loc);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,793 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.features.codecompare.decompile;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.features.codecompare.plugin.FunctionComparisonProvider;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.util.datastruct.Duo.Side;
|
||||||
|
|
||||||
|
public class DualDecompilerActionTest extends AbstractDualDecompilerTest {
|
||||||
|
private FunctionComparisonProvider provider = null;
|
||||||
|
|
||||||
|
private Program progDemangler24Debug;
|
||||||
|
private Function funcDemangler24DebugMain;
|
||||||
|
private Function funcDemangler24DebugCplusDemangle;
|
||||||
|
private static final long DEMANGLER_24_DEBUG_MAIN_OFFSET = 0x414835;
|
||||||
|
private static final long DEMANGLER_24_DEBUG_CPLUS_DEMANGLE_OFFSET = 0x40c01b;
|
||||||
|
private static final long DEMANGLER_24_DEBUG_INTERNAL_CPLUS_DEMANGLE_OFFSET = 0x40c987;
|
||||||
|
private static final String DEMANGLER_24_DEBUG_PROG_NAME =
|
||||||
|
"CodeCompare/demangler_gnu_v2_24_fulldebug";
|
||||||
|
|
||||||
|
private Program progDemangler24Stripped;
|
||||||
|
private Function funcDemangler24StrippedMain;
|
||||||
|
private Function funcDemangler24StrippedCplusDemangle;
|
||||||
|
private static final long DEMANGLER_24_STRIPPED_MAIN_OFFSET = 0x414835;
|
||||||
|
private static final long DEMANGLER_24_STRIPPED_CPLUS_DEMANGLE_OFFSET = 0x40c01b;
|
||||||
|
private static final long DEMANGLER_24_STRIPPED_INTERNAL_CPLUS_DEMANGLE_OFFSET = 0x40c987;
|
||||||
|
private static final String DEMANGLER_24_STRIPPED_PROG_NAME =
|
||||||
|
"CodeCompare/demangler_gnu_v2_24_stripped";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
progDemangler24Debug = env.getProgram(DEMANGLER_24_DEBUG_PROG_NAME);
|
||||||
|
progDemangler24Stripped = env.getProgram(DEMANGLER_24_STRIPPED_PROG_NAME);
|
||||||
|
|
||||||
|
assertNotNull(progDemangler24Debug);
|
||||||
|
assertNotNull(progDemangler24Stripped);
|
||||||
|
|
||||||
|
funcDemangler24DebugMain =
|
||||||
|
getFunctionFromOffset(progDemangler24Debug, DEMANGLER_24_DEBUG_MAIN_OFFSET);
|
||||||
|
funcDemangler24DebugCplusDemangle =
|
||||||
|
getFunctionFromOffset(progDemangler24Debug, DEMANGLER_24_DEBUG_CPLUS_DEMANGLE_OFFSET);
|
||||||
|
funcDemangler24StrippedMain =
|
||||||
|
getFunctionFromOffset(progDemangler24Stripped, DEMANGLER_24_STRIPPED_MAIN_OFFSET);
|
||||||
|
funcDemangler24StrippedCplusDemangle =
|
||||||
|
getFunctionFromOffset(progDemangler24Stripped,
|
||||||
|
DEMANGLER_24_STRIPPED_CPLUS_DEMANGLE_OFFSET);
|
||||||
|
|
||||||
|
assertNotNull(funcDemangler24DebugMain);
|
||||||
|
assertNotNull(funcDemangler24DebugCplusDemangle);
|
||||||
|
assertNotNull(funcDemangler24StrippedMain);
|
||||||
|
assertNotNull(funcDemangler24StrippedCplusDemangle);
|
||||||
|
|
||||||
|
showTool(fcPlugin.getTool());
|
||||||
|
env.open(progDemangler24Debug);
|
||||||
|
env.open(progDemangler24Stripped);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Comparisons of these functions are used in the next few tests
|
||||||
|
|
||||||
|
|
||||||
|
Decomp of 'main' in 'demangler_gnu_v2_24_fulldebug':
|
||||||
|
1|
|
||||||
|
2| int main(int argc,char **argv)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| char *pcVar1;
|
||||||
|
6| char **argv_local;
|
||||||
|
7| int argc_local;
|
||||||
|
8| char *options;
|
||||||
|
9| char *demangler;
|
||||||
|
10| int skip_first;
|
||||||
|
11| int i;
|
||||||
|
12| char *valid_symbols;
|
||||||
|
13| int c;
|
||||||
|
14|
|
||||||
|
15| demangler = (char *)0x0;
|
||||||
|
16| options = (char *)0x0;
|
||||||
|
17| program_name = *argv;
|
||||||
|
18| strip_underscore = prepends_underscore;
|
||||||
|
19| argv_local = argv;
|
||||||
|
20| argc_local = argc;
|
||||||
|
21| expandargv(&argc_local,&argv_local);
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
Decomp of 'FUN_00414835' (main) in 'demangler_gnu_v2_41_stripped':
|
||||||
|
1|
|
||||||
|
2| undefined8 FUN_00414835(int param_1,undefined8 *param_2)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| char *pcVar1;
|
||||||
|
6| undefined8 *local_48;
|
||||||
|
7| int local_3c [3];
|
||||||
|
8| undefined8 local_30;
|
||||||
|
9| undefined8 local_28;
|
||||||
|
10| int local_20;
|
||||||
|
11| int local_1c;
|
||||||
|
12| char *local_18;
|
||||||
|
13| uint local_c;
|
||||||
|
14|
|
||||||
|
15| local_28 = 0;
|
||||||
|
16| local_30 = 0;
|
||||||
|
17| DAT_004252e0 = *param_2;
|
||||||
|
18| DAT_0041d220 = DAT_00425240;
|
||||||
|
19| local_48 = param_2;
|
||||||
|
20| local_3c[0] = param_1;
|
||||||
|
21| FUN_00401a2d(local_3c,&local_48);
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
Decomp of 'FUN_0040c01b' (cplus_demangle) in 'demangler_gnu_v2_24_stripped':
|
||||||
|
1|
|
||||||
|
2| long FUN_0040c01b(undefined8 param_1,uint param_2)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| long lVar1;
|
||||||
|
6| uint local_88 [30];
|
||||||
|
7| long local_10;
|
||||||
|
8|
|
||||||
|
9| if (DAT_0041d150 == 0xffffffff) {
|
||||||
|
10| local_10 = FUN_0040b419(param_1);
|
||||||
|
11| }
|
||||||
|
12| else {
|
||||||
|
13| memset(local_88,0,0x70);
|
||||||
|
14| local_88[0] = param_2;
|
||||||
|
15| if ((param_2 & 0xff04) == 0) {
|
||||||
|
16| local_88[0] = DAT_0041d150 & 0xff04 | param_2;
|
||||||
|
17| }
|
||||||
|
...
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLocalNameTransferAction() throws RuntimeException {
|
||||||
|
final String actionName = ApplyLocalNameFromMatchedTokensAction.ACTION_NAME;
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
ClangToken currentToken;
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedCplusDemangle);
|
||||||
|
DockingActionIf localNameTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(localNameTransferAction);
|
||||||
|
|
||||||
|
line = 15;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(uncorrelatedPanel, Side.LEFT, line, col);
|
||||||
|
// Cursor is now on uncorrelated local variable token 'demangler'
|
||||||
|
// Ensure the local variable name transfer action is not active
|
||||||
|
assertEquals("demangler", currentToken.getText());
|
||||||
|
assertNotEnabled(localNameTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel correlatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedMain);
|
||||||
|
// Recreated provider, need to get new handle on action
|
||||||
|
localNameTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(localNameTransferAction);
|
||||||
|
|
||||||
|
line = 15;
|
||||||
|
col = 11;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated constant token '0'.
|
||||||
|
// Ensure the local variable name transfer action is not active
|
||||||
|
assertEquals("0", currentToken.getText());
|
||||||
|
assertNotEnabled(localNameTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 17;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated global variable token 'DAT_004252e0'
|
||||||
|
// Ensure the local variable name transfer action is not active
|
||||||
|
assertEquals("DAT_004252e0", currentToken.getText());
|
||||||
|
assertNotEnabled(localNameTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 15;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated local variable token 'local_28'
|
||||||
|
// Ensure the local variable name transfer action is active and working
|
||||||
|
assertEquals("local_28", currentToken.getText());
|
||||||
|
assertEnabled(localNameTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
performAction(localNameTransferAction);
|
||||||
|
waitForDecompile(correlatedPanel);
|
||||||
|
currentToken = getCurrentToken(correlatedPanel, Side.RIGHT);
|
||||||
|
assertEquals("demangler", currentToken.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGlobalNameTransferAction() throws RuntimeException {
|
||||||
|
final String actionName = ApplyGlobalNameFromMatchedTokensAction.ACTION_NAME;
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
ClangToken currentToken;
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedCplusDemangle);
|
||||||
|
DockingActionIf globalNameTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(globalNameTransferAction);
|
||||||
|
|
||||||
|
line = 17;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(uncorrelatedPanel, Side.LEFT, line, col);
|
||||||
|
// Cursor is now on uncorrelated global variable token 'program_name'
|
||||||
|
// Ensure the global variable name transfer action is not active
|
||||||
|
assertEquals("program_name", currentToken.getText());
|
||||||
|
assertNotEnabled(globalNameTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel correlatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedMain);
|
||||||
|
// Recreated provider, need to get new handle on action
|
||||||
|
globalNameTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(globalNameTransferAction);
|
||||||
|
|
||||||
|
line = 15;
|
||||||
|
col = 11;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated constant token '0'.
|
||||||
|
// Ensure the global variable name transfer action is not active
|
||||||
|
assertEquals("0", currentToken.getText());
|
||||||
|
assertNotEnabled(globalNameTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 15;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated local variable token 'local_28'
|
||||||
|
// Ensure the global variable name transfer action is not active
|
||||||
|
assertEquals("local_28", currentToken.getText());
|
||||||
|
assertNotEnabled(globalNameTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 17;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated global variable token 'DAT_004252e0'
|
||||||
|
// Ensure the global variable name transfer action is active and working
|
||||||
|
assertEquals("DAT_004252e0", currentToken.getText());
|
||||||
|
assertEnabled(globalNameTransferAction, getProviderContext());
|
||||||
|
performAction(globalNameTransferAction);
|
||||||
|
waitForDecompile(correlatedPanel);
|
||||||
|
currentToken = getCurrentToken(correlatedPanel, Side.RIGHT);
|
||||||
|
assertEquals("program_name", currentToken.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVariableTypeTransferAction() throws RuntimeException {
|
||||||
|
final String actionName = ApplyVariableTypeFromMatchedTokensAction.ACTION_NAME;
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
ClangToken currentToken;
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedCplusDemangle);
|
||||||
|
DockingActionIf typeTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(typeTransferAction);
|
||||||
|
|
||||||
|
line = 15;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(uncorrelatedPanel, Side.LEFT, line, col);
|
||||||
|
// Cursor is now on uncorrelated local variable token 'demangler'
|
||||||
|
// Ensure the variable type transfer action is not active
|
||||||
|
assertEquals("demangler", currentToken.getText());
|
||||||
|
assertNotEnabled(typeTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 17;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(uncorrelatedPanel, Side.LEFT, line, col);
|
||||||
|
// Cursor is now on uncorrelated global variable token 'program_name'
|
||||||
|
// Ensure the variable type transfer action is not active
|
||||||
|
assertEquals("program_name", currentToken.getText());
|
||||||
|
assertNotEnabled(typeTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel correlatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugMain, funcDemangler24StrippedMain);
|
||||||
|
// Recreated provider, need to get new handle on action
|
||||||
|
typeTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(typeTransferAction);
|
||||||
|
|
||||||
|
line = 15;
|
||||||
|
col = 11;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated constant token '0'.
|
||||||
|
// Ensure the variable type transfer action is not active
|
||||||
|
assertEquals("0", currentToken.getText());
|
||||||
|
assertNotEnabled(typeTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 15;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated local variable token 'local_28'
|
||||||
|
// Ensure the variable type transfer action is active and works on local variable
|
||||||
|
assertEquals("local_28", currentToken.getText());
|
||||||
|
assertEnabled(typeTransferAction, getProviderContext());
|
||||||
|
performAction(typeTransferAction);
|
||||||
|
waitForDecompile(correlatedPanel);
|
||||||
|
// Check that 'local_28 = 0;' has become 'local_28 = (char *)0x0;
|
||||||
|
col = 12;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
assertEquals("char", currentToken.getText());
|
||||||
|
col = 17;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
assertEquals("*", currentToken.getText());
|
||||||
|
|
||||||
|
line = 17;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated global variable token 'DAT_004252e0'
|
||||||
|
// Ensure the global variable name transfer action is active and working
|
||||||
|
assertEquals("DAT_004252e0", currentToken.getText());
|
||||||
|
assertEnabled(typeTransferAction, getProviderContext());
|
||||||
|
performAction(typeTransferAction);
|
||||||
|
waitForDecompile(correlatedPanel);
|
||||||
|
// Check that 'DAT_004252e0 = *param_2;' has become 'PTR_004252e0 = (char *)*param_2;'
|
||||||
|
currentToken = getCurrentToken(correlatedPanel, Side.RIGHT);
|
||||||
|
assertEquals("PTR_004252e0", currentToken.getText());
|
||||||
|
col = 16;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
assertEquals("char", currentToken.getText());
|
||||||
|
col = 21;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
assertEquals("*", currentToken.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The callee and struct type tests use the decomp of the following functions. We care about
|
||||||
|
* the work_stuff struct and the call to 'internal_cplus_demangle' / 'FUN_0040c987', which has
|
||||||
|
* debug signature: char * internal_cplus_demangle(work_stuff *work, char *mangled)
|
||||||
|
* stripped signature: undefined8 FUN_0040c987(uint *param_1,char *param_2):
|
||||||
|
|
||||||
|
Decomp of 'cplus_demangle' in 'demangler_gnu_v2_24_fulldebug':
|
||||||
|
1|
|
||||||
|
2| char * cplus_demangle(char *mangled,int options)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| char *pcVar1;
|
||||||
|
6| int options_local;
|
||||||
|
7| char *mangled_local;
|
||||||
|
8| work_stuff work [1];
|
||||||
|
9| char *ret;
|
||||||
|
10|
|
||||||
|
11| if (current_demangling_style == no_demangling) {
|
||||||
|
12| pcVar1 = xstrdup(mangled);
|
||||||
|
13| }
|
||||||
|
14| else {
|
||||||
|
15| memset(work,0,0x70);
|
||||||
|
16| work[0].options = options;
|
||||||
|
17| if ((options & 0xff04U) == 0) {
|
||||||
|
18| work[0].options =
|
||||||
|
19| current_demangling_style &
|
||||||
|
20| (gnat_demangling|gnu_v3_demangling|edg_demangling|hp_demangling|arm_demangling|
|
||||||
|
21| lucid_demangling|gnu_demangling|auto_demangling|java_demangling) | options;
|
||||||
|
22| }
|
||||||
|
23| if (((((work[0].options & 0x4000U) == 0) && ((work[0].options & 0x100U) == 0)) ||
|
||||||
|
24| ((pcVar1 = cplus_demangle_v3(mangled,work[0].options), pcVar1 == (char *)0x0 &&
|
||||||
|
25| ((work[0].options & 0x4000U) == 0)))) &&
|
||||||
|
26| (((work[0].options & 4U) == 0 || (pcVar1 = java_demangle_v3(mangled), pcVar1 == (char *)0x0))
|
||||||
|
27| )) {
|
||||||
|
28| if ((work[0].options & 0x8000U) == 0) {
|
||||||
|
29| pcVar1 = internal_cplus_demangle(work,mangled);
|
||||||
|
30| squangle_mop_up(work);
|
||||||
|
31| }
|
||||||
|
32| else {
|
||||||
|
33| pcVar1 = ada_demangle(mangled,options);
|
||||||
|
34| }
|
||||||
|
35| }
|
||||||
|
36| }
|
||||||
|
37| return pcVar1;
|
||||||
|
38| }
|
||||||
|
|
||||||
|
|
||||||
|
Decomp of 'FUN_0040c01b' (cplus_demangle) in 'demangler_gnu_v2_24_stripped':
|
||||||
|
1|
|
||||||
|
2| long FUN_0040c01b(undefined8 param_1,uint param_2)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| long lVar1;
|
||||||
|
6| uint local_88 [30];
|
||||||
|
7| long local_10;
|
||||||
|
8|
|
||||||
|
9| if (DAT_0041d150 == 0xffffffff) {
|
||||||
|
10| local_10 = FUN_0040b419(param_1);
|
||||||
|
11| }
|
||||||
|
12| else {
|
||||||
|
13| memset(local_88,0,0x70);
|
||||||
|
14| local_88[0] = param_2;
|
||||||
|
15| if ((param_2 & 0xff04) == 0) {
|
||||||
|
16| local_88[0] = DAT_0041d150 & 0xff04 | param_2;
|
||||||
|
17| }
|
||||||
|
18| if (((((local_88[0] & 0x4000) == 0) && (lVar1 = local_10, (local_88[0] & 0x100) == 0)) ||
|
||||||
|
19| ((local_10 = FUN_0040a8cc(param_1,local_88[0]), local_10 == 0 &&
|
||||||
|
20| (lVar1 = 0, (local_88[0] & 0x4000) == 0)))) &&
|
||||||
|
21| ((local_10 = lVar1, (local_88[0] & 4) == 0 ||
|
||||||
|
22| (local_10 = FUN_0040a922(param_1), local_10 == 0)))) {
|
||||||
|
23| if ((local_88[0] & 0x8000) == 0) {
|
||||||
|
24| local_10 = FUN_0040c987(local_88,param_1);
|
||||||
|
25| FUN_0040cb6c(local_88);
|
||||||
|
26| }
|
||||||
|
27| else {
|
||||||
|
28| local_10 = FUN_0040c154(param_1,param_2);
|
||||||
|
29| }
|
||||||
|
30| }
|
||||||
|
31| }
|
||||||
|
32| return local_10;
|
||||||
|
33| }
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFullStructTypeTransferAction() throws RuntimeException {
|
||||||
|
final String actionName = ApplyVariableTypeFromMatchedTokensAction.ACTION_NAME;
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
ClangToken currentToken;
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel correlatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedCplusDemangle);
|
||||||
|
DockingActionIf typeTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(typeTransferAction);
|
||||||
|
|
||||||
|
line = 14;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated local variable token 'local_88'.
|
||||||
|
// Ensure the variable type transfer action is active, and
|
||||||
|
assertEquals("local_88", currentToken.getText());
|
||||||
|
assertEnabled(typeTransferAction, getProviderContext());
|
||||||
|
performAction(typeTransferAction);
|
||||||
|
waitForDecompile(correlatedPanel);
|
||||||
|
|
||||||
|
// Sanity check: options field exists
|
||||||
|
line = 13;
|
||||||
|
col = 12;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
assertEquals("options", currentToken.getText());
|
||||||
|
|
||||||
|
// Check that the work_stuff struct is fully in the data type manager
|
||||||
|
ProgramBasedDataTypeManager debugDtm = progDemangler24Debug.getDataTypeManager();
|
||||||
|
Structure debugStructure =
|
||||||
|
(Structure) debugDtm.getDataType(new DataTypePath("/DWARF/cplus-dem.c", "work_stuff"));
|
||||||
|
assertNotNull(debugStructure);
|
||||||
|
|
||||||
|
ProgramBasedDataTypeManager strippedDtm = progDemangler24Stripped.getDataTypeManager();
|
||||||
|
Structure strippedStructure = (Structure) strippedDtm
|
||||||
|
.getDataType(new DataTypePath("/DWARF/cplus-dem.c", "work_stuff"));
|
||||||
|
assertNotNull(strippedStructure);
|
||||||
|
|
||||||
|
assertEquals(debugStructure.getNumComponents(), strippedStructure.getNumComponents());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSkeletonStructTypeTransferAction() throws RuntimeException {
|
||||||
|
final String actionName = ApplyEmptyVariableTypeFromMatchedTokensAction.ACTION_NAME;
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
ClangToken currentToken;
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel correlatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedCplusDemangle);
|
||||||
|
DockingActionIf typeTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(typeTransferAction);
|
||||||
|
|
||||||
|
line = 14;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
|
||||||
|
// Cursor is now on correlated local variable token 'local_88'.
|
||||||
|
// Ensure the variable type transfer action is active, and
|
||||||
|
assertEquals("local_88", currentToken.getText());
|
||||||
|
assertEnabled(typeTransferAction, getProviderContext());
|
||||||
|
performAction(typeTransferAction);
|
||||||
|
waitForDecompile(correlatedPanel);
|
||||||
|
|
||||||
|
// Sanity check: variable declaration is retyped
|
||||||
|
line = 6;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
assertEquals("work_stuff", currentToken.getText());
|
||||||
|
|
||||||
|
// Check that an empty struct is in the data type manager
|
||||||
|
ProgramBasedDataTypeManager dtm = progDemangler24Stripped.getDataTypeManager();
|
||||||
|
Structure structure =
|
||||||
|
(Structure) dtm.getDataType(new DataTypePath("/DWARF/cplus-dem.c", "work_stuff"));
|
||||||
|
|
||||||
|
assertNotNull(structure);
|
||||||
|
assertTrue(structure.isNotYetDefined());
|
||||||
|
assertTrue(structure.isZeroLength());
|
||||||
|
assertEquals(0, structure.getNumComponents());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalleeNameTransferAction() throws RuntimeException {
|
||||||
|
final String actionName = ApplyCalleeFunctionNameFromMatchedTokensAction.ACTION_NAME;
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
ClangToken currentToken;
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedMain);
|
||||||
|
DockingActionIf calleeNameTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(calleeNameTransferAction);
|
||||||
|
|
||||||
|
line = 12;
|
||||||
|
col = 9;
|
||||||
|
currentToken = setDecompLocation(uncorrelatedPanel, Side.LEFT, line, col);
|
||||||
|
// Cursor is now on uncorrelated callee token 'xstrdup'
|
||||||
|
// Ensure the callee name transfer action is not active
|
||||||
|
assertEquals("xstrdup", currentToken.getText());
|
||||||
|
assertNotEnabled(calleeNameTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel correlatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedCplusDemangle);
|
||||||
|
// Recreated provider, need to get new handle on action
|
||||||
|
calleeNameTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(calleeNameTransferAction);
|
||||||
|
|
||||||
|
line = 9;
|
||||||
|
col = 4;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated global variable token 'DAT_0041d150'
|
||||||
|
// Ensure the callee name transfer action is not active
|
||||||
|
assertEquals("DAT_0041d150", currentToken.getText());
|
||||||
|
assertNotEnabled(calleeNameTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 10;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated local variable token 'local_10'
|
||||||
|
// Ensure the callee name transfer action is not active
|
||||||
|
assertEquals("local_10", currentToken.getText());
|
||||||
|
assertNotEnabled(calleeNameTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 24;
|
||||||
|
col = 11;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated callee token 'FUN_0040c987'
|
||||||
|
// Ensure the callee name transfer action is active and working
|
||||||
|
assertEquals("FUN_0040c987", currentToken.getText());
|
||||||
|
assertEnabled(calleeNameTransferAction, getProviderContext());
|
||||||
|
performAction(calleeNameTransferAction);
|
||||||
|
waitForDecompile(correlatedPanel);
|
||||||
|
|
||||||
|
// Check updated function name
|
||||||
|
currentToken = getCurrentToken(correlatedPanel, Side.RIGHT);
|
||||||
|
assertEquals("internal_cplus_demangle", currentToken.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalleeFullTypeTransferAction() throws RuntimeException {
|
||||||
|
final String actionName =
|
||||||
|
ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction.ACTION_NAME;
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
ClangToken currentToken;
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedMain);
|
||||||
|
DockingActionIf calleeFullSignatureTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(calleeFullSignatureTransferAction);
|
||||||
|
|
||||||
|
line = 12;
|
||||||
|
col = 9;
|
||||||
|
currentToken = setDecompLocation(uncorrelatedPanel, Side.LEFT, line, col);
|
||||||
|
// Cursor is now on uncorrelated callee token 'xstrdup'
|
||||||
|
// Ensure the callee name transfer action is not active
|
||||||
|
assertEquals("xstrdup", currentToken.getText());
|
||||||
|
assertNotEnabled(calleeFullSignatureTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel correlatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedCplusDemangle);
|
||||||
|
// Recreated provider, need to get new handle on action
|
||||||
|
calleeFullSignatureTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(calleeFullSignatureTransferAction);
|
||||||
|
|
||||||
|
line = 9;
|
||||||
|
col = 4;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated global variable token 'DAT_0041d150'
|
||||||
|
// Ensure the callee name transfer action is not active
|
||||||
|
assertEquals("DAT_0041d150", currentToken.getText());
|
||||||
|
assertNotEnabled(calleeFullSignatureTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 10;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated local variable token 'local_10'
|
||||||
|
// Ensure the callee name transfer action is not active
|
||||||
|
assertEquals("local_10", currentToken.getText());
|
||||||
|
assertNotEnabled(calleeFullSignatureTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 24;
|
||||||
|
col = 11;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated callee token 'FUN_0040c987'
|
||||||
|
// Ensure the callee name transfer action is active and working
|
||||||
|
assertEquals("FUN_0040c987", currentToken.getText());
|
||||||
|
assertEnabled(calleeFullSignatureTransferAction, getProviderContext());
|
||||||
|
performAction(calleeFullSignatureTransferAction);
|
||||||
|
waitForDecompile(correlatedPanel);
|
||||||
|
|
||||||
|
// Check updated function name
|
||||||
|
line = 25;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
assertEquals("internal_cplus_demangle", currentToken.getText());
|
||||||
|
|
||||||
|
// Check updated function signature
|
||||||
|
FunctionSignature debugSig = getFunctionFromOffset(progDemangler24Debug,
|
||||||
|
DEMANGLER_24_DEBUG_INTERNAL_CPLUS_DEMANGLE_OFFSET).getSignature();
|
||||||
|
FunctionSignature strippedSig = getFunctionFromOffset(progDemangler24Stripped,
|
||||||
|
DEMANGLER_24_STRIPPED_INTERNAL_CPLUS_DEMANGLE_OFFSET).getSignature();
|
||||||
|
|
||||||
|
assertEquals(debugSig.getReturnType().getDisplayName(),
|
||||||
|
strippedSig.getReturnType().getDisplayName());
|
||||||
|
assertEquals(debugSig.getArguments().length, strippedSig.getArguments().length);
|
||||||
|
|
||||||
|
for (int i = 0; i < debugSig.getArguments().length; i++) {
|
||||||
|
ParameterDefinition p = debugSig.getArguments()[i];
|
||||||
|
ParameterDefinition p2 = strippedSig.getArguments()[i];
|
||||||
|
assertEquals(p.getDataType().getDisplayName(), p2.getDataType().getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check full datatype for work_stuff
|
||||||
|
ProgramBasedDataTypeManager debugDtm = progDemangler24Debug.getDataTypeManager();
|
||||||
|
Structure debugStructure =
|
||||||
|
(Structure) debugDtm.getDataType(new DataTypePath("/DWARF/cplus-dem.c", "work_stuff"));
|
||||||
|
assertNotNull(debugStructure);
|
||||||
|
|
||||||
|
ProgramBasedDataTypeManager strippedDtm = progDemangler24Stripped.getDataTypeManager();
|
||||||
|
Structure strippedStructure = (Structure) strippedDtm
|
||||||
|
.getDataType(new DataTypePath("/DWARF/cplus-dem.c", "work_stuff"));
|
||||||
|
assertNotNull(strippedStructure);
|
||||||
|
|
||||||
|
assertEquals(debugStructure.getNumComponents(), strippedStructure.getNumComponents());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCalleeSkeletonTypeTransferAction() throws RuntimeException {
|
||||||
|
final String actionName = ApplyCalleeEmptySignatureFromMatchedTokensAction.ACTION_NAME;
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
ClangToken currentToken;
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel uncorrelatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedMain);
|
||||||
|
DockingActionIf calleeFullSignatureTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(calleeFullSignatureTransferAction);
|
||||||
|
|
||||||
|
line = 12;
|
||||||
|
col = 9;
|
||||||
|
currentToken = setDecompLocation(uncorrelatedPanel, Side.LEFT, line, col);
|
||||||
|
// Cursor is now on uncorrelated callee token 'xstrdup'
|
||||||
|
// Ensure the callee name transfer action is not active
|
||||||
|
assertEquals("xstrdup", currentToken.getText());
|
||||||
|
assertNotEnabled(calleeFullSignatureTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel correlatedPanel =
|
||||||
|
preparePanel(funcDemangler24DebugCplusDemangle, funcDemangler24StrippedCplusDemangle);
|
||||||
|
// Recreated provider, need to get new handle on action
|
||||||
|
calleeFullSignatureTransferAction = getLocalAction(provider, actionName);
|
||||||
|
assertNotNull(calleeFullSignatureTransferAction);
|
||||||
|
|
||||||
|
line = 9;
|
||||||
|
col = 4;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated global variable token 'DAT_0041d150'
|
||||||
|
// Ensure the callee name transfer action is not active
|
||||||
|
assertEquals("DAT_0041d150", currentToken.getText());
|
||||||
|
assertNotEnabled(calleeFullSignatureTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 10;
|
||||||
|
col = 0;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated local variable token 'local_10'
|
||||||
|
// Ensure the callee name transfer action is not active
|
||||||
|
assertEquals("local_10", currentToken.getText());
|
||||||
|
assertNotEnabled(calleeFullSignatureTransferAction, getProviderContext());
|
||||||
|
|
||||||
|
line = 24;
|
||||||
|
col = 11;
|
||||||
|
currentToken = setDecompLocation(correlatedPanel, Side.RIGHT, line, col);
|
||||||
|
// Cursor is now on correlated callee token 'FUN_0040c987'
|
||||||
|
// Ensure the callee name transfer action is active and working
|
||||||
|
assertEquals("FUN_0040c987", currentToken.getText());
|
||||||
|
assertEnabled(calleeFullSignatureTransferAction, getProviderContext());
|
||||||
|
performAction(calleeFullSignatureTransferAction);
|
||||||
|
waitForDecompile(correlatedPanel);
|
||||||
|
|
||||||
|
// Check updated function name
|
||||||
|
currentToken = getCurrentToken(correlatedPanel, Side.RIGHT);
|
||||||
|
assertEquals("internal_cplus_demangle", currentToken.getText());
|
||||||
|
|
||||||
|
// Check updated function signature
|
||||||
|
FunctionSignature debugSig = getFunctionFromOffset(progDemangler24Debug,
|
||||||
|
DEMANGLER_24_DEBUG_INTERNAL_CPLUS_DEMANGLE_OFFSET).getSignature();
|
||||||
|
FunctionSignature strippedSig = getFunctionFromOffset(progDemangler24Stripped,
|
||||||
|
DEMANGLER_24_STRIPPED_INTERNAL_CPLUS_DEMANGLE_OFFSET).getSignature();
|
||||||
|
|
||||||
|
assertEquals(debugSig.getReturnType().getDisplayName(),
|
||||||
|
strippedSig.getReturnType().getDisplayName());
|
||||||
|
assertEquals(debugSig.getArguments().length, strippedSig.getArguments().length);
|
||||||
|
|
||||||
|
for (int i = 0; i < debugSig.getArguments().length; i++) {
|
||||||
|
ParameterDefinition p = debugSig.getArguments()[i];
|
||||||
|
ParameterDefinition p2 = strippedSig.getArguments()[i];
|
||||||
|
assertEquals(p.getDataType().getDisplayName(), p2.getDataType().getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that an empty struct is in the data type manager
|
||||||
|
ProgramBasedDataTypeManager dtm = progDemangler24Stripped.getDataTypeManager();
|
||||||
|
Structure structure =
|
||||||
|
(Structure) dtm.getDataType(new DataTypePath("/DWARF/cplus-dem.c", "work_stuff"));
|
||||||
|
|
||||||
|
assertNotNull(structure);
|
||||||
|
assertTrue(structure.isNotYetDefined());
|
||||||
|
assertTrue(structure.isZeroLength());
|
||||||
|
assertEquals(0, structure.getNumComponents());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup and focus to a decompiler comparison between the two selected functions. Wait for
|
||||||
|
// the decompilation to complete so that subsequent calls to navigation, etc. work correctly
|
||||||
|
private DecompilerCodeComparisonPanel preparePanel(Function leftFunc, Function rightFunc) {
|
||||||
|
if (provider != null) {
|
||||||
|
// Always want to clear out existing comparison
|
||||||
|
closeProvider(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
provider = compareFunctions(Set.of(leftFunc, rightFunc));
|
||||||
|
|
||||||
|
DecompilerCodeComparisonPanel decompPanel = findDecompilerPanel(provider);
|
||||||
|
waitForDecompile(decompPanel);
|
||||||
|
decompPanel.setSynchronizedScrolling(true);
|
||||||
|
setActivePanel(provider, decompPanel);
|
||||||
|
|
||||||
|
assertEquals(decompPanel.getFunction(Side.LEFT), leftFunc);
|
||||||
|
assertEquals(decompPanel.getFunction(Side.RIGHT), rightFunc);
|
||||||
|
|
||||||
|
return decompPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function getFunctionFromOffset(Program prog, long offset) {
|
||||||
|
return prog.getFunctionManager()
|
||||||
|
.getFunctionAt(
|
||||||
|
prog.getAddressFactory().getDefaultAddressSpace().getAddress(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertEnabled(DockingActionIf action, ActionContext context) {
|
||||||
|
assertTrue(runSwing(() -> action.isEnabledForContext(context)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNotEnabled(DockingActionIf action, ActionContext context) {
|
||||||
|
assertFalse(runSwing(() -> action.isEnabledForContext(context)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test programs are always opened read-only, so set context override
|
||||||
|
private ActionContext getProviderContext() {
|
||||||
|
ActionContext context = provider.getActionContext(null);
|
||||||
|
if (context instanceof DualDecompilerActionContext dualDecompContext) {
|
||||||
|
dualDecompContext.setOverrideReadOnly(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -35,8 +35,8 @@ import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.function.FunctionPlugin;
|
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||||
import ghidra.features.base.codecompare.model.FunctionComparisonModel;
|
import ghidra.features.base.codecompare.model.FunctionComparisonModel;
|
||||||
import ghidra.features.base.codecompare.model.MatchedFunctionComparisonModel;
|
import ghidra.features.base.codecompare.model.MatchedFunctionComparisonModel;
|
||||||
|
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||||
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
||||||
import ghidra.features.codecompare.plugin.*;
|
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.data.ByteDataType;
|
import ghidra.program.model.data.ByteDataType;
|
||||||
|
@ -130,12 +130,15 @@ public class CompareFunctionsProviderTest extends AbstractGhidraHeadedIntegratio
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNextPreviousActionSwitchPanelFocus() {
|
public void testNextPreviousActionSwitchPanelFocus() throws Exception {
|
||||||
Set<Function> functions = Set.of(foo, bar);
|
Set<Function> functions = Set.of(foo, bar);
|
||||||
provider = compareFunctions(functions);
|
provider = compareFunctions(functions);
|
||||||
DockingActionIf nextAction = getAction(plugin, "Compare Next Function");
|
DockingActionIf nextAction = getAction(plugin, "Compare Next Function");
|
||||||
DockingActionIf previousAction = getAction(plugin, "Compare Previous Function");
|
DockingActionIf previousAction = getAction(plugin, "Compare Previous Function");
|
||||||
|
|
||||||
|
// since we are clicking the listing panel, bring that to the front first
|
||||||
|
setActivePanel(provider, provider.getComponent().getDualListingPanel());
|
||||||
|
|
||||||
// left panel has focus, so nextAction should be enabled and previous should be disabled
|
// left panel has focus, so nextAction should be enabled and previous should be disabled
|
||||||
ActionContext context = provider.getActionContext(null);
|
ActionContext context = provider.getActionContext(null);
|
||||||
assertEnabled(nextAction, context);
|
assertEnabled(nextAction, context);
|
||||||
|
@ -288,6 +291,11 @@ public class CompareFunctionsProviderTest extends AbstractGhidraHeadedIntegratio
|
||||||
assertFalse(runSwing(() -> action.isEnabledForContext(context)));
|
assertFalse(runSwing(() -> action.isEnabledForContext(context)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setActivePanel(FunctionComparisonProvider provider, CodeComparisonPanel panel) {
|
||||||
|
runSwing(() -> provider.getComponent().setCurrentTabbedComponent(panel.getName()));
|
||||||
|
waitForSwing();
|
||||||
|
}
|
||||||
|
|
||||||
private FunctionComparisonProvider compareFunctions(Set<Function> functions) {
|
private FunctionComparisonProvider compareFunctions(Set<Function> functions) {
|
||||||
runSwing(() -> plugin.createComparison(functions));
|
runSwing(() -> plugin.createComparison(functions));
|
||||||
waitForSwing();
|
waitForSwing();
|
Loading…
Add table
Add a link
Reference in a new issue