diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/StringParameterPropagator.java b/Ghidra/Features/Decompiler/ghidra_scripts/StringParameterPropagator.java index 1cadb8e6df..91c2c0ca66 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/StringParameterPropagator.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/StringParameterPropagator.java @@ -36,6 +36,7 @@ import ghidra.program.model.data.*; import ghidra.program.model.lang.PrototypeModel; import ghidra.program.model.listing.*; import ghidra.program.model.pcode.*; +import ghidra.program.model.pcode.HighFunctionDBUtil.ReturnCommitOption; import ghidra.program.model.symbol.*; import ghidra.util.exception.*; @@ -195,9 +196,10 @@ public class StringParameterPropagator extends GhidraScript { int maxParams = funcInfo.getMaxParamsSeen(); boolean couldBeVararg = !funcInfo.numParamsAgree(); if (!funcInfo.numParamsAgree()) { - currentProgram.getBookmarkManager().setBookmark(calledFunc.getEntryPoint(), - BookmarkType.NOTE, this.getClass().getName(), - "Number of parameters disagree min: " + minParams + " max: " + maxParams); + currentProgram.getBookmarkManager() + .setBookmark(calledFunc.getEntryPoint(), BookmarkType.NOTE, + this.getClass().getName(), "Number of parameters disagree min: " + + minParams + " max: " + maxParams); println("WARNING : Number of params disagree for " + calledFunc.getName() + " @ " + entry); @@ -317,9 +319,8 @@ public class StringParameterPropagator extends GhidraScript { ReferenceIterator dataRefIter = rData.getReferenceIteratorTo(); while (dataRefIter.hasNext()) { Reference dataRef = dataRefIter.next(); - func = - currentProgram.getFunctionManager().getFunctionContaining( - dataRef.getFromAddress()); + func = currentProgram.getFunctionManager() + .getFunctionContaining(dataRef.getFromAddress()); if (func == null) { continue; } @@ -337,9 +338,8 @@ public class StringParameterPropagator extends GhidraScript { private void collectDataRefenceLocations(HashSet
dataItemLocationSet, HashSet
referringFuncLocationSet) { int count = 0; - ReferenceIterator iter = - currentProgram.getReferenceManager().getReferenceIterator( - currentProgram.getMinAddress()); + ReferenceIterator iter = currentProgram.getReferenceManager() + .getReferenceIterator(currentProgram.getMinAddress()); while (iter.hasNext() && !monitor.isCancelled()) { Reference ref = iter.next(); @@ -412,7 +412,8 @@ public class StringParameterPropagator extends GhidraScript { if (convention == null) { convention = currentProgram.getCompilerSpec().getDefaultCallingConvention(); } - if (initialConvention != null && !convention.getName().equals(initialConvention.getName())) { + if (initialConvention != null && + !convention.getName().equals(initialConvention.getName())) { return true; } @@ -452,8 +453,9 @@ public class StringParameterPropagator extends GhidraScript { if (param == null) { return false; } - currentProgram.getBookmarkManager().setBookmark(func.getEntryPoint(), BookmarkType.NOTE, - this.getClass().getName(), "Created " + dt.getName() + " parameter"); + currentProgram.getBookmarkManager() + .setBookmark(func.getEntryPoint(), BookmarkType.NOTE, this.getClass().getName(), + "Created " + dt.getName() + " parameter"); return false; } @@ -476,7 +478,8 @@ public class StringParameterPropagator extends GhidraScript { } if (minParams == numParams) { try { - HighFunctionDBUtil.commitParamsToDatabase(hfunction, true, SourceType.USER_DEFINED); + HighFunctionDBUtil.commitParamsToDatabase(hfunction, true, + ReturnCommitOption.NO_COMMIT, SourceType.USER_DEFINED); } catch (DuplicateNameException e) { throw new AssertException("Unexpected exception", e); @@ -497,9 +500,8 @@ public class StringParameterPropagator extends GhidraScript { if (i < f.getParameterCount()) { continue; } - VariableStorage storage = - convention.getArgLocation(i - 1, f.getParameters(), DataType.DEFAULT, - currentProgram); + VariableStorage storage = convention.getArgLocation(i - 1, f.getParameters(), + DataType.DEFAULT, currentProgram); if (storage.isUnassignedStorage()) { break; } @@ -576,7 +578,8 @@ public class StringParameterPropagator extends GhidraScript { } long mask = - 0xffffffffffffffffL >>> ((8 - entry.getAddressSpace().getPointerSize()) * 8); + 0xffffffffffffffffL >>> ((8 - entry.getAddressSpace().getPointerSize()) * + 8); Address possibleAddr = entry.getNewAddress(mask & value); if (stringLocationSet.contains(possibleAddr)) { markStringParam(constUse, possibleAddr, calledFuncAddr, i - 1, @@ -637,9 +640,8 @@ public class StringParameterPropagator extends GhidraScript { return true; try { - DecompileResults decompRes = - decompInterface.decompileFunction(f, - decompInterface.getOptions().getDefaultTimeout(), monitor); + DecompileResults decompRes = decompInterface.decompileFunction(f, + decompInterface.getOptions().getDefaultTimeout(), monitor); hfunction = decompRes.getHighFunction(); } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/cmd/function/DecompilerParameterIdCmd.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/cmd/function/DecompilerParameterIdCmd.java index 25b7e3ffe2..74d8b210ad 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/cmd/function/DecompilerParameterIdCmd.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/cmd/function/DecompilerParameterIdCmd.java @@ -32,6 +32,7 @@ import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.pcode.*; +import ghidra.program.model.pcode.HighFunctionDBUtil.ReturnCommitOption; import ghidra.program.model.symbol.SourceType; import ghidra.program.model.util.AcyclicCallGraphBuilder; import ghidra.util.Msg; @@ -215,17 +216,10 @@ public class DecompilerParameterIdCmd extends BackgroundCommand { if (hfunc == null) { return; } - HighFunctionDBUtil.commitParamsToDatabase(hfunc, true, SourceType.ANALYSIS); - boolean commitReturn = true; - if (!commitVoidReturn) { - DataType returnType = hfunc.getFunctionPrototype().getReturnType(); - if (returnType instanceof VoidDataType) { - commitReturn = false; - } - } - if (commitReturn) { - HighFunctionDBUtil.commitReturnToDatabase(hfunc, SourceType.ANALYSIS); - } + ReturnCommitOption returnCommit = commitVoidReturn ? ReturnCommitOption.COMMIT + : ReturnCommitOption.COMMIT_NO_VOID; + HighFunctionDBUtil.commitParamsToDatabase(hfunc, true, returnCommit, + SourceType.ANALYSIS); goodInfo = true; } else { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/CommitParamsAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/CommitParamsAction.java index ea090ac2cf..d8d0b6f6c8 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/CommitParamsAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/CommitParamsAction.java @@ -24,6 +24,7 @@ import ghidra.app.util.HelpTopics; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.HighFunction; import ghidra.program.model.pcode.HighFunctionDBUtil; +import ghidra.program.model.pcode.HighFunctionDBUtil.ReturnCommitOption; import ghidra.program.model.symbol.SourceType; import ghidra.util.HelpLocation; import ghidra.util.Msg; @@ -59,8 +60,8 @@ public class CommitParamsAction extends AbstractDecompilerAction { source = SourceType.USER_DEFINED; } - HighFunctionDBUtil.commitReturnToDatabase(hfunc, source); - HighFunctionDBUtil.commitParamsToDatabase(hfunc, true, source); + HighFunctionDBUtil.commitParamsToDatabase(hfunc, true, ReturnCommitOption.COMMIT, + source); } catch (DuplicateNameException e) { throw new AssertException("Unexpected exception", e); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RenameVariableTask.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RenameVariableTask.java index 99375d55e9..f36fc0819f 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RenameVariableTask.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RenameVariableTask.java @@ -21,6 +21,7 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.*; +import ghidra.program.model.pcode.HighFunctionDBUtil.ReturnCommitOption; import ghidra.program.model.symbol.SourceType; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.InvalidInputException; @@ -49,10 +50,8 @@ public class RenameVariableTask extends RenameTask { @Override public void commit() throws DuplicateNameException, InvalidInputException { if (commitRequired) { - HighFunctionDBUtil.commitParamsToDatabase(hfunction, false, signatureSrcType); - if (signatureSrcType != SourceType.DEFAULT) { - HighFunctionDBUtil.commitReturnToDatabase(hfunction, signatureSrcType); - } + HighFunctionDBUtil.commitParamsToDatabase(hfunction, false, + ReturnCommitOption.NO_COMMIT, signatureSrcType); } HighFunctionDBUtil.updateDBVariable(highSymbol, newName, null, srctype); } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeLocalAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeLocalAction.java index 8c7394c24a..93d9209012 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeLocalAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeLocalAction.java @@ -29,6 +29,7 @@ import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.*; +import ghidra.program.model.pcode.HighFunctionDBUtil.ReturnCommitOption; import ghidra.program.model.symbol.SourceType; import ghidra.util.*; import ghidra.util.exception.*; @@ -96,11 +97,7 @@ public class RetypeLocalAction extends AbstractDecompilerAction { hfunction.getFunction().getSignatureSource() != SourceType.DEFAULT; try { HighFunctionDBUtil.commitParamsToDatabase(hfunction, useDataTypes, - SourceType.USER_DEFINED); - if (useDataTypes) { - HighFunctionDBUtil.commitReturnToDatabase(hfunction, - SourceType.USER_DEFINED); - } + ReturnCommitOption.NO_COMMIT, SourceType.USER_DEFINED); } catch (DuplicateNameException e) { throw new AssertException("Unexpected exception", e); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeReturnAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeReturnAction.java index 37e29efbe1..2761d6ef8f 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeReturnAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeReturnAction.java @@ -32,6 +32,7 @@ import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.HighFunction; import ghidra.program.model.pcode.HighFunctionDBUtil; +import ghidra.program.model.pcode.HighFunctionDBUtil.ReturnCommitOption; import ghidra.program.model.symbol.SourceType; import ghidra.util.*; import ghidra.util.exception.*; @@ -97,7 +98,7 @@ public class RetypeReturnAction extends AbstractDecompilerAction { if (commitRequired) { try { HighFunctionDBUtil.commitParamsToDatabase(highFunction, true, - SourceType.USER_DEFINED); + ReturnCommitOption.NO_COMMIT, SourceType.USER_DEFINED); } catch (DuplicateNameException e) { throw new AssertException("Unexpected exception", e); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java index 682cac0430..2760af8c9b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java @@ -72,54 +72,109 @@ public class HighFunctionDBUtil { return modelName; } - /** - * Commit the decompiler's version of the function return data-type to the database. - * The decompiler's version of the prototype model is committed as well - * @param highFunction is the decompiler's model of the function - * @param source is the desired SourceType for the commit - */ - public static void commitReturnToDatabase(HighFunction highFunction, SourceType source) { - try { + public enum ReturnCommitOption { + /** + * {@link #NO_COMMIT} - keep functions existing return parameter + */ + NO_COMMIT, - // Change calling convention if needed - Function function = highFunction.getFunction(); - String convention = function.getCallingConventionName(); - String modelName = getPrototypeModelForCommit(highFunction); - if (modelName != null && !modelName.equals(convention)) { - function.setCallingConvention(modelName); - } + /** + * {@link #COMMIT} - commit return parameter as defined by {@link HighFunction} + */ + COMMIT, - VariableStorage storage = highFunction.getFunctionPrototype().getReturnStorage(); - DataType dataType = highFunction.getFunctionPrototype().getReturnType(); - if (dataType == null) { - dataType = DefaultDataType.dataType; - source = SourceType.DEFAULT; - } - function.setReturn(dataType, storage, source); - } - catch (InvalidInputException e) { - Msg.error(HighFunctionDBUtil.class, e.getMessage()); - } + /** + * {@value #COMMIT_NO_VOID} - commit return parameter as defined by {@link HighFunction} + * unless it is {@link VoidDataType} in which case keep existing function return parameter. + */ + COMMIT_NO_VOID; } /** - * Commit all parameters associated with HighFunction to the underlying database. + * Commit all parameters, including optional return, associated with HighFunction to the + * underlying database. * @param highFunction is the associated HighFunction * @param useDataTypes is true if the HighFunction's parameter data-types should be committed + * @param returnCommit controls optional commit of return parameter * @param source is the signature source type to set * @throws DuplicateNameException if commit of parameters caused conflict with other * local variable/label. * @throws InvalidInputException if specified storage is invalid */ public static void commitParamsToDatabase(HighFunction highFunction, boolean useDataTypes, - SourceType source) throws DuplicateNameException, InvalidInputException { + ReturnCommitOption returnCommit, SourceType source) + throws DuplicateNameException, InvalidInputException { Function function = highFunction.getFunction(); + Parameter returnParam; + if (returnCommit == ReturnCommitOption.NO_COMMIT) { + returnParam = function.getReturn(); + } + else { + returnParam = getReturnParameter(highFunction, useDataTypes, returnCommit); + } + List params = getParameters(highFunction, useDataTypes); + boolean hasVarArgs = highFunction.getFunctionPrototype().isVarArg(); String modelName = getPrototypeModelForCommit(highFunction); - commitParamsToDatabase(function, modelName, params, - highFunction.getFunctionPrototype().isVarArg(), true, source); + + try { + function.updateFunction(modelName, returnParam, params, + FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, source); + } + catch (DuplicateNameException e) { + for (Variable param : params) { + changeConflictingSymbolNames(param.getName(), returnParam, function); + } + function.updateFunction(modelName, null, params, + FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, source); + } + + boolean customStorageReqd = + !VariableUtilities.storageMatches(params, function.getParameters()); + if (returnCommit != ReturnCommitOption.NO_COMMIT) { + customStorageReqd |= + !returnParam.getVariableStorage().equals(function.getReturn().getVariableStorage()); + } + if (customStorageReqd) { + // try again if dynamic storage assignment does not match decompiler's + // force into custom storage mode + function.updateFunction(modelName, returnParam, params, + FunctionUpdateType.CUSTOM_STORAGE, true, source); + } + + if (function.hasVarArgs() != hasVarArgs) { + function.setVarArgs(hasVarArgs); + } + } + + private static Parameter getReturnParameter(HighFunction highFunction, boolean useDataTypes, + ReturnCommitOption returnCommit) throws InvalidInputException { + Function function = highFunction.getFunction(); + if (returnCommit == ReturnCommitOption.NO_COMMIT) { + return function.getReturn(); + } + + Program program = function.getProgram(); + DataTypeManager dtm = program.getDataTypeManager(); + + VariableStorage returnStorage = highFunction.getFunctionPrototype().getReturnStorage(); + DataType returnDt = highFunction.getFunctionPrototype().getReturnType(); + + if (useDataTypes && returnCommit == ReturnCommitOption.COMMIT_NO_VOID && + (returnDt instanceof VoidDataType)) { + return function.getReturn(); // retain current return + } + + if (returnDt == null) { + returnDt = DefaultDataType.dataType; + returnStorage = VariableStorage.UNASSIGNED_STORAGE; + } + else if (!useDataTypes) { + returnDt = Undefined.getUndefinedDataType(returnDt.getLength()).clone(dtm); + } + return new ReturnParameterImpl(returnDt, returnStorage, program); } private static List getParameters(HighFunction highFunction, boolean useDataTypes) @@ -146,54 +201,6 @@ public class HighFunctionDBUtil { return params; } - /** - * Commit a specified set of parameters for the given function to the database. - * The name, data-type, and storage is committed for each parameter. The parameters are - * provided along with a formal PrototypeModel. If the parameters fit the model, they are - * committed using "dynamic" storage. Otherwise, they are committed using "custom" storage. - * @param function is the Function being modified - * @param modelName is the name of the underlying PrototypeModel - * @param params is the formal list of parameter objects - * @param hasVarArgs is true if the prototype can take variable arguments - * @param renameConflicts if true any name conflicts will be resolved - * by renaming the conflicting local variable/label - * @param source source type - * @throws DuplicateNameException if commit of parameters caused conflict with other - * local variable/label. Should not occur if renameConflicts is true. - * @throws InvalidInputException for invalid variable names or for parameter data-types that aren't fixed length - * @throws DuplicateNameException is there are collisions between variable names in the function's scope - */ - public static void commitParamsToDatabase(Function function, String modelName, - List params, boolean hasVarArgs, boolean renameConflicts, SourceType source) - throws DuplicateNameException, InvalidInputException { - - try { - function.updateFunction(modelName, null, params, - FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, source); - } - catch (DuplicateNameException e) { - if (!renameConflicts) { - throw e; - } - for (Variable param : params) { - changeConflictingSymbolNames(param.getName(), null, function); - } - function.updateFunction(modelName, null, params, - FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, source); - } - - if (!VariableUtilities.storageMatches(params, function.getParameters())) { - // try again if dynamic storage assignment does not match decompiler's - // force into custom storage mode - function.updateFunction(modelName, null, params, FunctionUpdateType.CUSTOM_STORAGE, - true, source); - } - - if (function.hasVarArgs() != hasVarArgs) { - function.setVarArgs(hasVarArgs); - } - } - private static void changeConflictingSymbolNames(String name, Variable ignoreVariable, Function func) { @@ -454,7 +461,8 @@ public class HighFunctionDBUtil { if (slot >= parameters.length || !parameters[slot].getVariableStorage().equals(param.getStorage())) { try { - commitParamsToDatabase(highFunction, true, SourceType.ANALYSIS); + commitParamsToDatabase(highFunction, true, ReturnCommitOption.NO_COMMIT, + SourceType.ANALYSIS); } catch (DuplicateNameException e) { throw new AssertException("Unexpected exception", e);