mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +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
|
||||
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
|
||||
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>
|
||||
|
||||
<P class="providedbyplugin">Provided By: <I>FunctionComparisonPlugin</I></P>
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -27,8 +27,8 @@ import ghidra.util.Msg;
|
|||
* comparison window
|
||||
*/
|
||||
public abstract class AbstractFunctionComparisonApplyAction extends DockingAction {
|
||||
protected static final String MENU_PARENT = "Apply From Other";
|
||||
protected static final String MENU_GROUP = "A0_Apply";
|
||||
protected static final String MENU_PARENT = "Apply From Other Function";
|
||||
protected static final String MENU_GROUP = "A0_ApplyFunction";
|
||||
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;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import docking.ActionContext;
|
||||
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}
|
||||
*/
|
||||
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 boolean disableOnReadOnly;
|
||||
|
@ -52,83 +43,43 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
|
|||
this.disableOnReadOnly = disableOnReadOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the action should be enable for a pair of
|
||||
* matching tokens.
|
||||
*
|
||||
* @param tokenPair tokens
|
||||
* @return true if action should be enabled
|
||||
*/
|
||||
protected abstract boolean enabledForTokens(TokenPair tokenPair);
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
if (!(context instanceof DualDecompilerActionContext compareContext)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dualDecompilerActionPerformed(compareContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof DualDecompilerActionContext compareContext)) {
|
||||
return false;
|
||||
}
|
||||
DecompilerCodeComparisonPanel decompPanel = compareContext.getCodeComparisonPanel();
|
||||
|
||||
if (disableOnReadOnly) {
|
||||
//get the program corresponding to the panel with focus
|
||||
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
|
||||
if (compareContext.isActiveProgramReadOnly()) {
|
||||
return false; // program is read-only, don't enable action
|
||||
}
|
||||
}
|
||||
|
||||
TokenPair currentPair = getCurrentTokenPair(decompPanel);
|
||||
return enabledForTokens(currentPair);
|
||||
|
||||
return isEnabledForDualDecompilerContext(compareContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link TokenPair} consisting of the token under the cursor in the focused
|
||||
* decompiler panel and its counterpart in the other panel.
|
||||
*
|
||||
* @param decompPanel decomp panel
|
||||
* @return matching tokens (or null if no match)
|
||||
* Subclasses return true if they are enabled for the given context
|
||||
*
|
||||
* @param context the context
|
||||
* @return true if enabled
|
||||
*/
|
||||
protected TokenPair getCurrentTokenPair(
|
||||
DecompilerCodeComparisonPanel decompPanel) {
|
||||
protected abstract boolean isEnabledForDualDecompilerContext(
|
||||
DualDecompilerActionContext context);
|
||||
|
||||
DecompilerPanel focusedPanel = decompPanel.getActiveDisplay().getDecompilerPanel();
|
||||
|
||||
if (!(focusedPanel.getCurrentLocation() instanceof DecompilerLocation focusedLocation)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* Subclasses will perform their work in this method
|
||||
* @param context the context
|
||||
*/
|
||||
protected abstract void dualDecompilerActionPerformed(DualDecompilerActionContext context);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -15,17 +15,10 @@
|
|||
*/
|
||||
package ghidra.features.codecompare.decompile;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.decompiler.ClangFuncNameToken;
|
||||
import ghidra.app.services.FunctionComparisonService;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
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.HelpLocation;
|
||||
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
|
||||
* tokens.
|
||||
*/
|
||||
public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAction {
|
||||
public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedCalleeTokensAction {
|
||||
private PluginTool tool;
|
||||
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
|
||||
|
@ -48,58 +39,14 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAc
|
|||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, false);
|
||||
this.tool = tool;
|
||||
FunctionComparisonService service = tool.getService(FunctionComparisonService.class);
|
||||
if (service != null) {
|
||||
MenuData menuData = new MenuData(new String[] { ACTION_NAME }, null, MENU_GROUP);
|
||||
setPopupMenuData(menuData);
|
||||
setEnabled(true);
|
||||
setHelpLocation(new HelpLocation(HELP_TOPIC, "Compare Matching Callees"));
|
||||
}
|
||||
|
||||
MenuData menuData = new MenuData(new String[] { ACTION_NAME }, null, MENU_GROUP);
|
||||
setPopupMenuData(menuData);
|
||||
setHelpLocation(new HelpLocation(HELP_TOPIC, "Compare Matching Callees"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean enabledForTokens(TokenPair tokenPair) {
|
||||
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;
|
||||
}
|
||||
|
||||
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||
FunctionComparisonService service = tool.getService(FunctionComparisonService.class);
|
||||
if (service == null) {
|
||||
Msg.error(this, "Function Comparison Service not found!");
|
||||
|
@ -108,30 +55,4 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAc
|
|||
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(toggleExactConstantMatchingAction);
|
||||
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) {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -15,11 +15,22 @@
|
|||
*/
|
||||
package ghidra.features.codecompare.decompile;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
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.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.
|
||||
|
@ -28,6 +39,8 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
implements RestrictedAddressSetContext {
|
||||
|
||||
private DecompilerCodeComparisonPanel decompilerComparisonPanel = null;
|
||||
private TokenPair tokenPair;
|
||||
private boolean overrideReadOnly = false;
|
||||
|
||||
/**
|
||||
* Creates an action context for a dual decompiler panel.
|
||||
|
@ -38,7 +51,47 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
public DualDecompilerActionContext(ComponentProvider provider,
|
||||
DecompilerCodeComparisonPanel panel, Component 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() {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -35,8 +35,8 @@ import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
|||
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||
import ghidra.features.base.codecompare.model.FunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.model.MatchedFunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
||||
import ghidra.features.codecompare.plugin.*;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.ByteDataType;
|
||||
|
@ -130,12 +130,15 @@ public class CompareFunctionsProviderTest extends AbstractGhidraHeadedIntegratio
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testNextPreviousActionSwitchPanelFocus() {
|
||||
public void testNextPreviousActionSwitchPanelFocus() throws Exception {
|
||||
Set<Function> functions = Set.of(foo, bar);
|
||||
provider = compareFunctions(functions);
|
||||
DockingActionIf nextAction = getAction(plugin, "Compare Next 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
|
||||
ActionContext context = provider.getActionContext(null);
|
||||
assertEnabled(nextAction, context);
|
||||
|
@ -288,6 +291,11 @@ public class CompareFunctionsProviderTest extends AbstractGhidraHeadedIntegratio
|
|||
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) {
|
||||
runSwing(() -> plugin.createComparison(functions));
|
||||
waitForSwing();
|
Loading…
Add table
Add a link
Reference in a new issue