Merge remote-tracking branch

'origin/GP-4263_ghidra1_EditSignatureOverride--SQUASHED' (Closes #6000)
This commit is contained in:
Ryan Kurtz 2024-02-01 08:08:19 -05:00
commit 54e2910694
8 changed files with 243 additions and 93 deletions

View File

@ -905,6 +905,9 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
OverridePrototypeAction overrideSigAction = new OverridePrototypeAction();
setGroupInfo(overrideSigAction, functionGroup, subGroupPosition++);
EditPrototypeOverrideAction editOverrideSigAction = new EditPrototypeOverrideAction();
setGroupInfo(editOverrideSigAction, functionGroup, subGroupPosition++);
DeletePrototypeOverrideAction deleteSigAction = new DeletePrototypeOverrideAction();
setGroupInfo(deleteSigAction, functionGroup, subGroupPosition++);
@ -1144,6 +1147,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
addLocalAction(editDataTypeAction);
addLocalAction(specifyCProtoAction);
addLocalAction(overrideSigAction);
addLocalAction(editOverrideSigAction);
addLocalAction(deleteSigAction);
addLocalAction(renameFunctionAction);
addLocalAction(renameLabelAction);

View File

@ -159,7 +159,7 @@ public abstract class AbstractDecompilerAction extends DockingAction {
}
/**
* Get the function corresponding to the specified decompiler context.
* Get the function corresponding to the specified decompiler context token.
*
* @param context decompiler action context
* @return the function associated with the current context token or null if none identified.

View File

@ -16,16 +16,13 @@
package ghidra.app.plugin.core.decompile.actions;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.data.ProgramBasedDataTypeManager;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.symbol.*;
import ghidra.program.model.pcode.DataTypeSymbol;
import ghidra.program.model.pcode.HighFunctionDBUtil;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.HelpLocation;
import ghidra.util.UndefinedFunction;
@ -37,38 +34,6 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
setPopupMenuData(new MenuData(new String[] { "Remove Signature Override" }, "Decompile"));
}
public static CodeSymbol getSymbol(Function func, ClangToken tokenAtCursor) {
if (tokenAtCursor == null) {
return null;
}
Namespace overspace = HighFunction.findOverrideSpace(func);
if (overspace == null) {
return null;
}
PcodeOp op = OverridePrototypeAction.getCallOp(func.getProgram(), tokenAtCursor);
if (op == null) {
return null;
}
SymbolTable symtab = func.getProgram().getSymbolTable();
SymbolIterator iter = symtab.getSymbolsAsIterator(op.getSeqnum().getTarget());
while (iter.hasNext()) {
Symbol sym = iter.next();
if (!sym.getName().startsWith("prt")) {
continue;
}
if (!(sym instanceof CodeSymbol)) {
continue;
}
if (!sym.getParentNamespace().equals(overspace)) {
continue;
}
return (CodeSymbol) sym;
}
return null;
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
@ -77,22 +42,26 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
return false;
}
return getSymbol(function, context.getTokenAtCursor()) != null;
return OverridePrototypeAction.getSymbol(function, context.getTokenAtCursor()) != null;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Function func = context.getFunction();
CodeSymbol sym = getSymbol(func, context.getTokenAtCursor());
Symbol sym = OverridePrototypeAction.getSymbol(func, context.getTokenAtCursor());
if (sym == null) {
return;
}
Program program = func.getProgram();
SymbolTable symtab = program.getSymbolTable();
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
int txId = program.startTransaction("Remove Override Signature");
try {
symtab.removeSymbolSpecial(sym);
DataTypeSymbol dts = HighFunctionDBUtil.readOverride(sym);
sym.delete();
dts.cleanupUnusedOverride();
}
finally {
program.endTransaction(txId, true);
}
}
}

View File

@ -0,0 +1,108 @@
/* ###
* 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.app.plugin.core.decompile.actions;
import docking.action.MenuData;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.FunctionDefinition;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.*;
import ghidra.util.exception.DuplicateNameException;
public class EditPrototypeOverrideAction extends AbstractDecompilerAction {
public EditPrototypeOverrideAction() {
super("Edit Signature Override");
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionEditOverride"));
setPopupMenuData(new MenuData(new String[] { "Edit Signature Override" }, "Decompile"));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function == null || function instanceof UndefinedFunction) {
return false;
}
Symbol sym = OverridePrototypeAction.getSymbol(function, context.getTokenAtCursor());
if (sym == null) {
return false;
}
return HighFunctionDBUtil.readOverride(sym) != null;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Function function = context.getFunction();
Symbol sym = OverridePrototypeAction.getSymbol(function, context.getTokenAtCursor());
if (sym == null) {
return;
}
DataTypeSymbol dts = HighFunctionDBUtil.readOverride(sym);
if (dts == null) {
return;
}
Function func = context.getFunction();
Program program = func.getProgram();
PcodeOp op = OverridePrototypeAction.getCallOp(program, context.getTokenAtCursor());
Function calledFunc = null;
if (op != null) {
calledFunc = OverridePrototypeAction.getCalledFunction(program, op);
}
FunctionDefinition updatedFuncDef = null;
try {
// Copy is used for edit so we can adjust name
FunctionDefinition funcDef =
(FunctionDefinition) dts.getDataType().copy(program.getDataTypeManager());
funcDef.setName(calledFunc != null ? calledFunc.getName() : "func");
updatedFuncDef = OverridePrototypeAction.editSignature(context, calledFunc,
funcDef.getPrototypeString());
if (updatedFuncDef == null) {
return;
}
// TODO: should use comparison to see if funcDef was changed.
// Should be able to use equals method after fixing category and name, however
// it does not check param names.
}
catch (InvalidNameException | DuplicateNameException e) {
Msg.error(this, "Unexpected error", e);
}
int transaction = program.startTransaction("Override Signature");
try {
Address addr = sym.getAddress();
sym.delete(); // delete old marker symbol
HighFunctionDBUtil.writeOverride(func, addr, updatedFuncDef);
dts.cleanupUnusedOverride();
}
catch (Exception e) {
Msg.showError(getClass(), context.getDecompilerPanel(), "Override Signature Failed",
"Error overriding signature: " + e);
}
finally {
program.endTransaction(transaction, true);
}
}
}

View File

@ -28,8 +28,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.*;
import ghidra.util.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
@ -48,7 +47,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
* @param tokenAtCursor is the point in the window the user has selected
* @return the PcodeOp or null
*/
public static PcodeOp getCallOp(Program program, ClangToken tokenAtCursor) {
static PcodeOp getCallOp(Program program, ClangToken tokenAtCursor) {
if (tokenAtCursor == null) {
return null;
}
@ -76,6 +75,38 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
return null;
}
static Symbol getSymbol(Function func, ClangToken tokenAtCursor) {
if (tokenAtCursor == null) {
return null;
}
Namespace overspace = HighFunction.findOverrideSpace(func);
if (overspace == null) {
return null;
}
PcodeOp op = getCallOp(func.getProgram(), tokenAtCursor);
if (op == null) {
return null;
}
SymbolTable symtab = func.getProgram().getSymbolTable();
SymbolIterator iter = symtab.getSymbolsAsIterator(op.getSeqnum().getTarget());
while (iter.hasNext()) {
Symbol sym = iter.next();
if (sym.getSymbolType() != SymbolType.LABEL) {
continue;
}
if (!sym.getParentNamespace().equals(overspace)) {
continue;
}
if (!sym.getName().startsWith("prt")) {
continue;
}
return sym;
}
return null;
}
private static PcodeOp getOpForAddress(Program program, Address addr, ClangToken token) {
ClangFunction cfunc = token.getClangFunction();
@ -114,7 +145,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
return opCode == PcodeOp.CALL || opCode == PcodeOp.CALLIND;
}
private Function getCalledFunction(Program program, PcodeOp op) {
static Function getCalledFunction(Program program, PcodeOp op) {
if (op.getOpcode() != PcodeOp.CALL) {
return null;
}
@ -226,8 +257,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
}
// don't enable if override already in place
return DeletePrototypeOverrideAction.getSymbol(context.getFunction(),
context.getTokenAtCursor()) == null;
return getSymbol(context.getFunction(), context.getTokenAtCursor()) == null;
}
@Override
@ -235,10 +265,10 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
Function func = context.getFunction();
Program program = func.getProgram();
PcodeOp op = getCallOp(program, context.getTokenAtCursor());
Function calledfunc = getCalledFunction(program, op);
Function calledFunc = getCalledFunction(program, op);
boolean varargs = false;
if (calledfunc != null) {
varargs = calledfunc.hasVarArgs();
if (calledFunc != null) {
varargs = calledFunc.hasVarArgs();
}
if ((op.getOpcode() == PcodeOp.CALL) && !varargs) {
if (OptionDialog.showOptionDialog(context.getDecompilerPanel(),
@ -250,26 +280,21 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
return;
}
}
Address addr = op.getSeqnum().getTarget();
String name = "func"; // Default if we don't have a real name
String conv = program.getCompilerSpec().getDefaultCallingConvention().getName();
if (calledfunc != null) {
name = calledfunc.getName();
conv = calledfunc.getCallingConventionName();
}
String signature = generateSignature(op, name, calledfunc);
PluginTool tool = context.getTool();
ProtoOverrideDialog dialog =
new ProtoOverrideDialog(tool, calledfunc != null ? calledfunc : func, signature, conv);
tool.showDialog(dialog);
FunctionDefinition fdef = dialog.getFunctionDefinition();
String name = "func"; // Default if we don't have a real name
if (calledFunc != null) {
name = calledFunc.getName();
}
String signature = generateSignature(op, name, calledFunc);
FunctionDefinition fdef = editSignature(context, calledFunc, signature);
if (fdef == null) {
return;
}
int transaction = program.startTransaction("Override Signature");
boolean commit = false;
try {
Address addr = op.getSeqnum().getTarget();
HighFunctionDBUtil.writeOverride(func, addr, fdef);
commit = true;
}
@ -282,13 +307,30 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
}
}
static FunctionDefinition editSignature(DecompilerActionContext context, Function calledFunc,
String signature) {
Function func = context.getFunction();
Program program = func.getProgram();
PluginTool tool = context.getTool();
String conv = program.getCompilerSpec().getDefaultCallingConvention().getName();
if (calledFunc != null) {
conv = calledFunc.getCallingConventionName();
}
ProtoOverrideDialog dialog =
new ProtoOverrideDialog(tool, calledFunc != null ? calledFunc : func, signature, conv);
tool.showDialog(dialog);
return dialog.getFunctionDefinition();
}
/**
* <code>ProtoOverrideDialog</code> provides the ability to edit the
* function signature associated with a specific function definition override
* at a sub-function callsite.
* Use of this editor requires the presence of the tool-based datatype manager service.
*/
private class ProtoOverrideDialog extends EditFunctionSignatureDialog {
private static class ProtoOverrideDialog extends EditFunctionSignatureDialog {
private FunctionDefinition functionDefinition;
private final String initialSignature;
private final String initialConvention;

View File

@ -139,34 +139,23 @@ public class SpecifyCPrototypeAction extends AbstractDecompilerAction {
return fsig;
}
/**
* Get function affected by specified action context
*
* @param function is the current decompiled function which will be the default if no other
* function identified by context token.
* @param context decompiler action context
* @return the function associated with the current context token. If no function corresponds
* to context token the decompiled function will be returned.
*/
private Function getFunction(Function function, DecompilerActionContext context) {
// try to look up the function that is at the current cursor location
// If there isn't one, just use the function we are in.
Function tokenFunction = getFunction(context);
return tokenFunction != null ? tokenFunction : function;
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function instanceof UndefinedFunction) {
Function decompiledFunction = context.getFunction();
Function func = getFunction(context);
if (func == null || (func instanceof UndefinedFunction)) {
return false;
}
return getFunction(function, context) != null;
if (func != decompiledFunction && OverridePrototypeAction.getSymbol(decompiledFunction,
context.getTokenAtCursor()) != null) {
return false; // disable action for sub-function call w/ override
}
return true;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Function function = getFunction(context.getFunction(), context);
Function function = getFunction(context);
PluginTool tool = context.getTool();
DataTypeManagerService service = tool.getService(DataTypeManagerService.class);

View File

@ -21,10 +21,12 @@ import generic.hash.SimpleCRC32;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.FunctionSignature;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.*;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
public class DataTypeSymbol {
private Symbol sym; // Traditional symbol object
@ -105,14 +107,12 @@ public class DataTypeSymbol {
public static void deleteSymbols(String nmroot, Address addr, SymbolTable symtab,
Namespace space) throws InvalidInputException {
ArrayList<Symbol> dellist = new ArrayList<Symbol>();
SymbolIterator iter = symtab.getSymbols(space);
while (iter.hasNext()) {
Symbol sym = iter.next();
for (Symbol sym : symtab.getSymbols(addr)) {
if (!sym.getName().startsWith(nmroot))
continue;
if (sym.getSymbolType() != SymbolType.LABEL)
continue;
if (!addr.equals(sym.getAddress()))
if (space.equals(sym.getParentNamespace()))
continue;
if (sym.hasReferences())
throw new InvalidInputException("DataTypeSymbol has a reference");
@ -123,6 +123,34 @@ public class DataTypeSymbol {
}
}
public void cleanupUnusedOverride() {
if (sym == null) {
throw new RuntimeException("not instantiated with readSymbol method");
}
// NOTE: Although the symbol may have just been deleted its name will still be
// be accesible within its retained DB record.
String overrideName = sym.getName(); // override marker symbol
Program program = sym.getProgram();
SymbolTable symbolTable = program.getSymbolTable();
String prefix = nmroot + "_";
String hashSuffix = "_" + extractHash(overrideName);
for (Symbol s : symbolTable.scanSymbolsByName(prefix)) {
String n = s.getName();
if (!n.startsWith(prefix)) {
break; // stop scan
}
if (s.getSymbolType() == SymbolType.LABEL && n.endsWith(hashSuffix) &&
HighFunction.isOverrideNamespace(s.getParentNamespace())) {
return; // do nothing if any symbol found
}
}
// remove unused override signature
program.getDataTypeManager().remove(getDataType(), TaskMonitor.DUMMY);
}
public static DataTypeSymbol readSymbol(String cat, Symbol s) {
if (s.getSymbolType() != SymbolType.LABEL) {
throw new IllegalArgumentException("Expected CODE symbol");

View File

@ -40,6 +40,8 @@ import ghidra.util.exception.InvalidInputException;
*/
public class HighFunction extends PcodeSyntaxTree {
public final static String DECOMPILER_TAG_MAP = "decompiler_tags";
public final static String OVERRIDE_NAMESPACE_NAME = "override";
private Function func; // The traditional function object
private Language language;
private CompilerSpec compilerSpec;
@ -485,14 +487,22 @@ public class HighFunction extends PcodeSyntaxTree {
}
}
public static boolean isOverrideNamespace(Namespace namespace) {
if (!OVERRIDE_NAMESPACE_NAME.equals(namespace.getName())) {
return false;
}
Namespace parent = namespace.getParentNamespace();
return (parent instanceof Function);
}
public static Namespace findOverrideSpace(Function func) {
SymbolTable symtab = func.getProgram().getSymbolTable();
return findNamespace(symtab, func, "override");
return findNamespace(symtab, func, OVERRIDE_NAMESPACE_NAME);
}
public static Namespace findCreateOverrideSpace(Function func) {
SymbolTable symtab = func.getProgram().getSymbolTable();
return findCreateNamespace(symtab, func, "override");
return findCreateNamespace(symtab, func, OVERRIDE_NAMESPACE_NAME);
}
public static Namespace findNamespace(SymbolTable symtab, Namespace parent, String name) {