GP-5996 - Version Tracking - Added option for apply function namespace

This commit is contained in:
dragonmacher
2025-10-03 19:00:44 -04:00
parent c997049e1f
commit 18f176152c
15 changed files with 2137 additions and 533 deletions
@@ -261,27 +261,27 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
*
* @param <T> the return type
* @param <E> the exception type
* @param p the program
* @param dobj the program or other domain object
* @param s the code to execute
* @return the supplier's return value
* @see #modifyProgram(Program, ExceptionalCallback)
* @see #modifyProgram(Program, ExceptionalFunction)
*/
public static <T, E extends Exception> T tx(Program p, ExceptionalSupplier<T, E> s) {
int txId = p.startTransaction("Test - Function in Transaction");
public static <T, E extends Exception> T tx(DomainObject dobj, ExceptionalSupplier<T, E> s) {
int txId = dobj.startTransaction("Test - Function in Transaction");
boolean commit = true;
try {
T t = s.get();
p.flushEvents();
dobj.flushEvents();
waitForSwing();
return t;
}
catch (Exception e) {
commit = false;
failWithException("Exception modifying program '" + p.getName() + "'", e);
failWithException("Exception modifying program '" + dobj.getName() + "'", e);
}
finally {
p.endTransaction(txId, commit);
dobj.endTransaction(txId, commit);
}
return null;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 57 KiB

@@ -164,13 +164,18 @@
<ul>
<li><b>Do Not Apply</b> - Do not apply markup to the destination program</li>
<li><b>Add</b> - Adds the function name from the source program to the one already
at the address in the destination program.</li>
at the address in the destination program. If the existing primary
symbol is default, then no new label will not be added, but instead
the existing label will be replaced with the source name.</li>
<li><b>Add As Primary</b> - Adds the function name from the source program to
the one already at the address in the destination program. Sets the
primary label in the destination program to whatever label was the
primary one in the source program.</li>
<li><b>Replace Always</b> - Always apply markup to from the source
program to the destination program.</li>
primary one in the source program. </li>
<li><b>Replace Always</b> - Always use the source symbol name to replace
the destination symbol name. The one exception is that if the
source symbol is a default symbol, then the destination symbol
name will be removed instead of being replaced with the source
symbol name.</li>
<li><b>Replace Default Only</b> - Only apply markup from the
source program to the destination program when the label
in the destination program is a default label.</li>
@@ -425,6 +430,40 @@
</TD>
</TR>
<TR>
<TD><b>Function Signature</b> : <br>
Specifies the default action to take when applying the function
signature of a match.
</TD>
<TD nowrap><tt>Replace When Same Parameter Count</tt>
</TD>
</TR>
<TR>
<TD><b>Replace Namespace</b> : <br>
Specifies whether to apply the source function's namespace when applying
the function name.
<P>
<B>Note: If the source uses the Global namespace, then the destination
will not be updated when this option is on. The Global namespace is
considered the default namesapce. The assumption is that if the
destination program has a non-Global namespace, then the existing
destination namespace is preferred over the Global namespace.
</B>
</P>
</TD>
<TD nowrap><tt>False</tt>
</TD>
</TR>
<TR>
<TD><b>Inline</b> : <br>
Specifies the action to take for applying the inline flag when
@@ -497,17 +536,7 @@
<TD nowrap><tt>False</tt>
</TD>
</TR>
<TR>
<TD><b>Function Signature</b> : <br>
Specifies the default action to take when applying the function
signature of a match.
</TD>
<TD nowrap><tt>Replace When Same Parameter Count</tt>
</TD>
</TR>
<TR>
<TD><b>Labels</b> : <br>
Specifies the default action to take when applying the label of a match.
@@ -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.
@@ -115,33 +115,37 @@ public class MarkupItemImpl implements VTMarkupItem {
@Override
public VTMarkupType getMarkupType() {
return markupItemStorage.getMarkupType();
return markupType;
}
@Override
public VTMarkupItemStatus getStatus() {
validateDestinationCache();
VTMarkupItemStatus status = markupItemStorage.getStatus();
if (status == UNAPPLIED) {
if (!gettingStatus) {
try {
gettingStatus = true;
boolean conflictsWithOtherMarkup = getMarkupType().conflictsWithOtherMarkup(
this, getAssociation().getMarkupItems(TaskMonitor.DUMMY));
if (conflictsWithOtherMarkup) {
return CONFLICT;
}
}
catch (CancelledException e) {
// Shouldn't happen, but ignore if it does.
}
finally {
gettingStatus = false;
if (status != UNAPPLIED) {
return status;
}
if (!gettingStatus) {
try {
gettingStatus = true;
VTAssociation association = getAssociation();
Collection<VTMarkupItem> items = association.getMarkupItems(TaskMonitor.DUMMY);
boolean conflicts = markupType.conflictsWithOtherMarkup(this, items);
if (conflicts) {
return CONFLICT;
}
}
if (hasSameSourceDestinationValues()) {
return SAME;
catch (CancelledException e) {
// Shouldn't happen, but ignore if it does.
}
finally {
gettingStatus = false;
}
}
if (hasSameSourceDestinationValues()) {
return SAME;
}
return status;
}
@@ -196,15 +200,10 @@ public class MarkupItemImpl implements VTMarkupItem {
markupItemStorage.setApplyFailed("Can't apply without a valid destination");
fireMarkupItemStatusChanged(oldStatus, FAILED_APPLY);
throw new VersionTrackingApplyException(
"Cannot apply a markup item without first " + "setting the destination address");
"Cannot apply a markup item without first setting the destination address");
}
if (!canApply()) {
// TODO Should this throw an Exception instead?
// VTMarkupType itemMarkupType = item.getMarkupType();
// throw new VersionTrackingApplyException("Cannot apply " +
// itemMarkupType.getDisplayName() + " at " + item.getDestinationAddress().toString() +
// ".");
return;
}
@@ -1,13 +1,12 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
@@ -16,13 +15,19 @@
*/
package ghidra.feature.vt.api.markuptype;
import static ghidra.feature.vt.gui.util.VTOptionDefines.*;
import java.util.*;
import ghidra.feature.vt.api.impl.MarkupItemImpl;
import ghidra.feature.vt.api.main.*;
import ghidra.feature.vt.api.stringable.FunctionSignatureStringable;
import ghidra.feature.vt.api.util.Stringable;
import ghidra.feature.vt.api.util.VersionTrackingApplyException;
import ghidra.feature.vt.gui.util.*;
import ghidra.feature.vt.gui.util.VTMatchApplyChoices.FunctionSignatureChoices;
import ghidra.feature.vt.gui.plugin.VTController;
import ghidra.feature.vt.gui.util.VTMatchApplyChoices;
import ghidra.feature.vt.gui.util.VTMatchApplyChoices.*;
import ghidra.feature.vt.gui.util.VTOptionDefines;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address;
@@ -30,16 +35,12 @@ import ghidra.program.model.listing.*;
import ghidra.program.util.*;
import ghidra.util.SystemUtilities;
import java.util.*;
public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstractMarkupType {
//==================================================================================================
// Factory Methods
//==================================================================================================
public static final VTMarkupType INSTANCE = new FunctionSignatureMarkupType();
@Override
public List<VTMarkupItem> createMarkupItems(VTAssociation association) {
@@ -67,6 +68,10 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
// End Factory Methods
//==================================================================================================
public static final VTMarkupType INSTANCE = new FunctionSignatureMarkupType();
private ToolOptions unapplyOptions;
private FunctionSignatureMarkupType() {
super("Function Signature");
}
@@ -106,8 +111,42 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
return;
}
destinationSignatureStringable.applyFunctionSignature(destinationFunction,
VT_UNAPPLY_MARKUP_OPTIONS, true);
ToolOptions options = getUnapplyOptions();
destinationSignatureStringable.applyFunctionSignature(destinationFunction, options, true);
}
private ToolOptions getUnapplyOptions() {
if (unapplyOptions != null) {
return unapplyOptions;
}
unapplyOptions = new ToolOptions(VTController.VERSION_TRACKING_OPTIONS_NAME);
unapplyOptions.setEnum(FUNCTION_SIGNATURE, FunctionSignatureChoices.REPLACE);
unapplyOptions.setEnum(CALLING_CONVENTION, CallingConventionChoices.NAME_MATCH);
unapplyOptions.setEnum(INLINE, ReplaceChoices.REPLACE);
unapplyOptions.setEnum(NO_RETURN, ReplaceChoices.REPLACE);
unapplyOptions.setEnum(VAR_ARGS, ReplaceChoices.REPLACE);
unapplyOptions.setEnum(CALL_FIXUP, ReplaceChoices.REPLACE);
unapplyOptions.setEnum(FUNCTION_RETURN_TYPE, ParameterDataTypeChoices.REPLACE);
unapplyOptions.setEnum(PARAMETER_DATA_TYPES, ParameterDataTypeChoices.REPLACE);
unapplyOptions.setEnum(PARAMETER_NAMES, SourcePriorityChoices.REPLACE);
unapplyOptions.setEnum(PARAMETER_COMMENTS, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(FUNCTION_NAME, FunctionNameChoices.REPLACE_ALWAYS);
unapplyOptions.setEnum(LABELS, LabelChoices.REPLACE_ALL);
unapplyOptions.setEnum(PLATE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(PRE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(END_OF_LINE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(REPEATABLE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(POST_COMMENT, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(DATA_MATCH_DATA_TYPE,
ReplaceDataChoices.REPLACE_ALL_DATA);
return unapplyOptions;
}
@Override
@@ -122,24 +161,6 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
markupItem.getMarkupType().getDisplayName() + " since it is excluded.");
}
ToolOptions adjustedOptions = markupOptions.copy();
// switch (functionSignatureChoice) {
// case REPLACE:
// adjustedOptions.putEnum(VTOptionDefines.FUNCTION_SIGNATURE,
// FunctionSignatureChoices.REPLACE);
// break;
// case WHEN_SAME_PARAMETER_COUNT:
// adjustedOptions.putEnum(VTOptionDefines.FUNCTION_SIGNATURE,
// FunctionSignatureChoices.WHEN_SAME_PARAMETER_COUNT);
// break;
// case WHEN_TAKING_SIGNATURE:
// // Don't apply the names. The function signature will do it if needed.
// return false;
// default:
// throw new IllegalArgumentException("Unsupported apply action: " + applyAction);
//
// }
Address destinationAddress = markupItem.getDestinationAddress();
if (destinationAddress == null) {
@@ -147,7 +168,8 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
}
if (destinationAddress == Address.NO_ADDRESS) {
throw new VersionTrackingApplyException("The destination address cannot be No Address!");
throw new VersionTrackingApplyException(
"The destination address cannot be No Address!");
}
Program destinationProgram = getDestinationProgram(markupItem.getAssociation());
@@ -166,11 +188,7 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
throw new VersionTrackingApplyException(
"Couldn't find destination function to apply a name.");
}
if (sourceStringable.applyFunctionSignature(destinationFunction, adjustedOptions, false)) {
// // If the function signature was applied, apply the names if necessary.
// applyParameterNamesIfNeeded(markupItem, adjustedOptions);
// // If the function signature was applied, apply the no return flag if necessary.
// applyNoReturnIfNeeded(markupItem, adjustedOptions);
if (sourceStringable.applyFunctionSignature(destinationFunction, markupOptions, false)) {
return true;
}
return false;
@@ -223,8 +241,9 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
}
Address entryAddress = function.getEntryPoint();
Stringable value =
isSource ? getSourceValue(association, address) : getCurrentDestinationValue(
association, address);
isSource ? getSourceValue(association, address)
: getCurrentDestinationValue(
association, address);
String displayString = (value != null) ? value.getDisplayString() : null;
return new FunctionReturnTypeFieldLocation(program, entryAddress, displayString);
}
@@ -284,6 +303,10 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
options.setEnum(VTOptionDefines.FUNCTION_SIGNATURE,
FunctionSignatureChoices.REPLACE);
break;
case REPLACE_FIRST_ONLY:
break;
default:
break;
}
return options;
}
@@ -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,8 +15,6 @@
*/
package ghidra.feature.vt.api.markuptype;
import static ghidra.feature.vt.gui.util.VTOptionDefines.*;
import java.util.Collection;
import java.util.List;
@@ -24,8 +22,6 @@ import ghidra.feature.vt.api.impl.MarkupItemImpl;
import ghidra.feature.vt.api.main.*;
import ghidra.feature.vt.api.util.Stringable;
import ghidra.feature.vt.api.util.VersionTrackingApplyException;
import ghidra.feature.vt.gui.plugin.VTController;
import ghidra.feature.vt.gui.util.VTMatchApplyChoices.*;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address;
@@ -36,33 +32,6 @@ import ghidra.util.task.TaskMonitor;
public abstract class VTMarkupType {
static final ToolOptions VT_UNAPPLY_MARKUP_OPTIONS =
new ToolOptions(VTController.VERSION_TRACKING_OPTIONS_NAME);
static {
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(FUNCTION_SIGNATURE, FunctionSignatureChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(CALLING_CONVENTION, CallingConventionChoices.NAME_MATCH);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(INLINE, ReplaceChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(NO_RETURN, ReplaceChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(VAR_ARGS, ReplaceChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(CALL_FIXUP, ReplaceChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(FUNCTION_RETURN_TYPE, ParameterDataTypeChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(PARAMETER_DATA_TYPES, ParameterDataTypeChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(PARAMETER_NAMES, SourcePriorityChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(PARAMETER_COMMENTS, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(FUNCTION_NAME, FunctionNameChoices.REPLACE_ALWAYS);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(LABELS, LabelChoices.REPLACE_ALL);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(PLATE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(PRE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(END_OF_LINE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(REPEATABLE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(POST_COMMENT, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(DATA_MATCH_DATA_TYPE,
ReplaceDataChoices.REPLACE_ALL_DATA);
}
private final String name;
public VTMarkupType(String name) {
@@ -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.
@@ -17,6 +17,7 @@ package ghidra.feature.vt.api.stringable;
import java.util.*;
import generic.json.Json;
import ghidra.app.cmd.label.SetLabelPrimaryCmd;
import ghidra.app.util.NamespaceUtils;
import ghidra.feature.vt.api.util.Stringable;
@@ -45,8 +46,10 @@ public class FunctionNameStringable extends Stringable {
if (symbol == null) {
return;
}
this.symbolName = symbol.getName();
this.sourceType = symbol.getSource();
Namespace namespace = symbol.getParentNamespace();
while (namespace != null) {
if (namespace instanceof GlobalNamespace) {
@@ -84,11 +87,11 @@ public class FunctionNameStringable extends Stringable {
sourceType = SourceType.valueOf(sourceName);
while (tok.hasMoreTokens()) {
getNamespaceInfo(tok);
addNamespaceInfo(tok);
}
}
private void getNamespaceInfo(StringTokenizer tok) {
private void addNamespaceInfo(StringTokenizer tok) {
String name = tok.nextToken();
int id = Integer.parseInt(tok.nextToken());
SymbolType type = SymbolType.getSymbolType(id);
@@ -97,64 +100,187 @@ public class FunctionNameStringable extends Stringable {
namespaceInfos.add(new NamespaceInfo(name, type, nameSpaceSourceType));
}
public void applyFunctionName(Program program, Function function)
// Note: this is only meant to be called on the 'destination stringable'
public void unapplyFunctionNameAndNamespace(Function targetFunction)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
Address entryPoint = function.getEntryPoint();
Namespace ns = createAllNamespacesAndClasses(targetFunction, namespaceInfos);
doApplyFunctionName(targetFunction, ns);
}
// Note: this is only meant to be called on the 'source stringable'
public void applyFunctionNameAndNamespace(Function targetFunction)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
// use this stringable's namespace info to create the equivalent namespace in the target
Namespace ns = createAllNamespacesAndClasses(targetFunction, namespaceInfos);
if (ns instanceof GlobalNamespace) {
Namespace targetNs = targetFunction.getParentNamespace();
if (!(targetNs instanceof GlobalNamespace)) {
// Assume for now that any non-global namespace in the target is preferred by the
// user to the default global namespace. We can always add another option for this
// behavior in the future. If we change this code, then update the help.
ns = targetNs;
}
}
doApplyFunctionName(targetFunction, ns);
}
// Note: this is only meant to be called on the 'source stringable'
public void applyFunctionName(Function targetFunction)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
// apply only the name; keep the existing namespace
Symbol s = targetFunction.getSymbol();
Namespace ns = s.getParentNamespace();
doApplyFunctionName(targetFunction, ns);
}
private void doApplyFunctionName(Function targetFunction, Namespace namespace)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
Symbol s = targetFunction.getSymbol();
Program targetProgram = targetFunction.getProgram();
SymbolTable symbolTable = targetProgram.getSymbolTable();
Address entryPoint = targetFunction.getEntryPoint();
if (entryPoint.isMemoryAddress() && sourceType == SourceType.DEFAULT) {
// Apply a default name by removing the current one.
program.getSymbolTable().removeSymbolSpecial(function.getSymbol());
symbolTable.removeSymbolSpecial(targetFunction.getSymbol());
s.setNamespace(namespace);
return;
}
SymbolTable symbolTable = program.getSymbolTable();
Namespace namespace = program.getGlobalNamespace();
for (NamespaceInfo info : namespaceInfos) {
Namespace ns = symbolTable.getNamespace(info.name, namespace);
if (ns != null) {
if (function.isExternal() != ns.isExternal()) {
throw new DuplicateNameException("Conflicting namespace: " + info.name);
}
if (info.symbolType == SymbolType.CLASS &&
ns.getSymbol().getSymbolType() == SymbolType.NAMESPACE) {
// Promote existing namespace to class
ns = NamespaceUtils.convertNamespaceToClass(ns);
}
namespace = ns;
}
else {
namespace = createNamespace(program, info, namespace);
}
}
Symbol s = function.getSymbol();
s.setNameAndNamespace(symbolName, namespace, sourceType);
}
public void addFunctionName(Program program, Function function, boolean isPrimary)
private Namespace createAllNamespacesAndClasses(Function destFunction,
List<NamespaceInfo> infos) throws DuplicateNameException, InvalidInputException {
Program destProgram = destFunction.getProgram();
SymbolTable symbolTable = destProgram.getSymbolTable();
Namespace namespace = destProgram.getGlobalNamespace();
for (NamespaceInfo info : infos) {
Namespace ns = symbolTable.getNamespace(info.name, namespace);
if (ns == null) {
namespace = createNamespace(destProgram, info, namespace);
continue;
}
if (destFunction.isExternal() != ns.isExternal()) {
throw new DuplicateNameException("Conflicting namespace: " + info.name);
}
if (info.symbolType == SymbolType.CLASS &&
ns.getSymbol().getSymbolType() == SymbolType.NAMESPACE) {
// Promote existing namespace to class
ns = NamespaceUtils.convertNamespaceToClass(ns);
}
namespace = ns;
}
return namespace;
}
public void addFunctionNameAndNamespace(Function sourceFunction,
Function destFunction, boolean isPrimary)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
List<NamespaceInfo> infos = new ArrayList<>();
Namespace namespace = sourceFunction.getParentNamespace();
while (namespace != null) {
if (namespace instanceof GlobalNamespace) {
break;
}
infos.add(new NamespaceInfo(namespace));
namespace = namespace.getParentNamespace();
}
Collections.reverse(infos);
doAddFunctionName(destFunction, infos, isPrimary);
}
public void addFunctionName(Function destFunction, boolean isPrimary)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
doAddFunctionName(destFunction, List.of(), isPrimary);
}
private void doAddFunctionName(Function destFunction, List<NamespaceInfo> namespaces,
boolean isPrimary)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
Program destProgram = destFunction.getProgram();
SymbolTable symbolTable = destProgram.getSymbolTable();
Namespace namespace = createAllNamespaces(destProgram, namespaces);
Symbol symbol = destFunction.getSymbol();
if (symbol.getSource() == SourceType.DEFAULT) {
// Special case: when the destination is default, replace the default symbol instead of
// adding a new symbol.
applyFunctionName(destFunction);
return;
}
Address entryPoint = destFunction.getEntryPoint();
Symbol addedSymbol = symbolTable.createLabel(entryPoint, symbolName, namespace, sourceType);
if (!isPrimary || addedSymbol == null) {
return;
}
Address address = addedSymbol.getAddress();
String name = addedSymbol.getName();
Namespace ns = addedSymbol.getParentNamespace();
SetLabelPrimaryCmd cmd = new SetLabelPrimaryCmd(address, name, ns);
cmd.applyTo(destProgram);
}
public String getSymbolNamespace() {
if (namespaceInfos.isEmpty()) {
return null;
}
StringBuilder buffy = new StringBuilder();
for (NamespaceInfo info : namespaceInfos) {
buffy.append(info.name).append(Namespace.DELIMITER);
}
int end = buffy.length();
int n = Namespace.DELIMITER.length();
buffy.delete(end - n, end);
return buffy.toString();
}
public String getSymbolName() {
return getSymbolName(false);
}
public String getSymbolName(boolean includeNamespace) {
if (!includeNamespace) {
return symbolName;
}
StringBuilder buffy = new StringBuilder();
for (NamespaceInfo info : namespaceInfos) {
buffy.append(info.name).append(Namespace.DELIMITER);
}
buffy.append(symbolName);
return buffy.toString();
}
public SourceType getSymbolSourceType() {
return sourceType;
}
private Namespace createAllNamespaces(Program program, List<NamespaceInfo> namespaces)
throws DuplicateNameException, InvalidInputException {
SymbolTable symbolTable = program.getSymbolTable();
Namespace namespace = program.getGlobalNamespace();
for (NamespaceInfo info : namespaceInfos) {
Namespace ns = symbolTable.getNamespace(info.name, namespace);
namespace = ns != null ? ns : createNamespace(program, info, namespace);
}
Symbol functionSymbol = function.getSymbol();
if (functionSymbol.getSource() == SourceType.DEFAULT) {
function.getSymbol().setNameAndNamespace(symbolName, namespace, sourceType);
}
else {
// Add a label.
Symbol addedSymbol = symbolTable.createLabel(function.getEntryPoint(), symbolName,
namespace, sourceType);
if (isPrimary && addedSymbol != null) {
SetLabelPrimaryCmd setLabelPrimaryCmd =
new SetLabelPrimaryCmd(addedSymbol.getAddress(), addedSymbol.getName(),
addedSymbol.getParentNamespace());
setLabelPrimaryCmd.applyTo(program);
}
for (NamespaceInfo info : namespaces) {
Namespace nextNs = symbolTable.getNamespace(info.name, namespace);
namespace = nextNs != null ? nextNs : createNamespace(program, info, namespace);
}
return namespace;
}
private Namespace createNamespace(Program program, NamespaceInfo info, Namespace namespace)
@@ -209,33 +335,52 @@ public class FunctionNameStringable extends Stringable {
return true;
}
//==================================================================================================
// Inner Classes
//==================================================================================================
//==================================================================================================
// Inner Classes
//==================================================================================================
private static class NamespaceInfo {
String name;
SymbolType symbolType;
SourceType sourceType;
public NamespaceInfo(Namespace namespace) {
private String name;
private SymbolType symbolType;
private SourceType sourceType;
NamespaceInfo(Namespace namespace) {
this.name = namespace.getName();
this.symbolType = namespace.getSymbol().getSymbolType();
this.sourceType = namespace.getSymbol().getSource();
}
public NamespaceInfo(String name, SymbolType type, SourceType sourceType) {
NamespaceInfo(String name, SymbolType type, SourceType sourceType) {
this.name = name;
this.symbolType = type;
this.sourceType = sourceType;
}
}
@Override
public String toString() {
return Json.toString(this);
}
public String getSymbolName() {
return symbolName;
}
@Override
public int hashCode() {
return Objects.hash(name, sourceType, symbolType);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
NamespaceInfo other = (NamespaceInfo) obj;
return Objects.equals(name, other.name) && sourceType == other.sourceType &&
Objects.equals(symbolType, other.symbolType);
}
public SourceType getSymbolSourceType() {
return sourceType;
}
}
@@ -19,7 +19,6 @@ import static ghidra.feature.vt.gui.util.VTOptionDefines.*;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -59,6 +58,8 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
private static final String FUNCTION_SIGNATURE_TOOLTIP =
"<html>The apply action for the <b>function signature</b> " +
"when performing bulk apply operations</html>";
private static final String USE_NAMESPACE_TOOLTIP =
"<html>When true function namespaces will be applied when names are applied.";
private static final String PLATE_COMMENT_TOOLTIP =
"<html>The apply action for <b>plate comments</b> when performing bulk apply operations</html>";
private static final String PRE_COMMENT_TOOLTIP =
@@ -119,6 +120,7 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
private JLabel dataMatchDataTypeLabel;
private JLabel functionNameLabel;
private JLabel functionSignatureLabel;
private JLabel useFunctionNamespaceLabel;
private JLabel returnTypeLabel;
private JLabel inlineLabel;
private JLabel noReturnLabel;
@@ -138,6 +140,7 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
private JComboBox<Enum<?>> dataMatchDataTypeComboBox;
private JComboBox<Enum<?>> functionNameComboBox;
private JComboBox<Enum<?>> functionSignatureComboBox;
private JCheckBox useFunctionNamespaceCheckBox;
private JComboBox<Enum<?>> returnTypeComboBox;
private JComboBox<Enum<?>> callingConventionComboBox;
private JComboBox<Enum<?>> inlineComboBox;
@@ -160,8 +163,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
private JCheckBox ignoreExcludedCheckBox;
private JCheckBox ignoreIncompleteCheckBox;
private ActionListener defaultActionListener;
private ToolOptions originalOptions;
private PropertyChangeListener listener;
private boolean unappliedChanges = false;
@@ -206,7 +207,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
panel.add(createSeparator());
panel.add(createIgnoreCheckBoxPanel());
// TODO More needs to be done with the layout here.
panel.setBorder(BorderFactory.createTitledBorder("Apply Markup Options"));
JScrollPane scrollPane = new JScrollPane(panel);
@@ -223,7 +223,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
private Component createIgnoreCheckBoxPanel() {
createIgnoreCheckBoxes();
setupIgnoreMarkupItemsListeners();
JPanel panel = new JPanel();
panel.add(ignoreExcludedCheckBox);
@@ -239,16 +238,10 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
ignoreExcludedCheckBox.setToolTipText(IGNORE_EXCLUDED_TOOLTIP);
}
private void setupIgnoreMarkupItemsListeners() {
ignoreExcludedCheckBox.addActionListener(defaultActionListener);
ignoreIncompleteCheckBox.addActionListener(defaultActionListener);
}
private JPanel createFunctionSignatureSubPanel() {
createFunctionSignatureDetailLabels();
createFunctionSignatureDetailChoices();
setupFunctionSignatureDetailChoiceListeners();
JPanel outerPanel = new JPanel();
outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
@@ -325,19 +318,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
varArgsComboBox.setToolTipText(VAR_ARGS_TOOLTIP);
}
private void setupFunctionSignatureDetailChoiceListeners() {
returnTypeComboBox.addActionListener(defaultActionListener);
inlineComboBox.addActionListener(defaultActionListener);
noReturnComboBox.addActionListener(defaultActionListener);
callingConventionComboBox.addActionListener(defaultActionListener);
callFixupComboBox.addActionListener(defaultActionListener);
varArgsComboBox.addActionListener(defaultActionListener);
}
private JPanel createParametersSubPanel() {
createParameterLabels();
createParameterChoices();
setupParameterChoiceListeners();
JPanel outerPanel = new JPanel();
outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
@@ -402,15 +385,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
PARAMETER_NAMES_REPLACE_IF_SAME_PRIORITY_TOOLTIP);
}
private void setupParameterChoiceListeners() {
parameterDataTypesComboBox.addActionListener(defaultActionListener);
parameterNamesComboBox.addActionListener(defaultActionListener);
userHighestPriorityRB.addActionListener(defaultActionListener);
importHighestPriorityRB.addActionListener(defaultActionListener);
replaceIfSameSourceCheckBox.addActionListener(defaultActionListener);
parameterCommentsComboBox.addActionListener(defaultActionListener);
}
private JPanel createPrioritySubPanel() {
JPanel outerPanel = new JPanel();
JPanel panel = new JPanel();
@@ -436,7 +410,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
createNonCommentMarkupLabels();
createNonCommentMarkupChoices();
setupNonCommentMarkupChoiceListeners();
JPanel outerPanel = new JPanel();
outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
@@ -452,6 +425,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
panel.add(functionSignatureLabel);
panel.add(functionSignatureComboBox);
panel.add(useFunctionNamespaceLabel);
panel.add(useFunctionNamespaceCheckBox);
outerPanel.add(panel);
return outerPanel;
}
@@ -468,6 +444,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
functionSignatureLabel = new GDLabel("Function Signature", SwingConstants.RIGHT);
functionSignatureLabel.setToolTipText(FUNCTION_SIGNATURE_TOOLTIP);
useFunctionNamespaceLabel = new GDLabel("Replace Namespace", SwingConstants.RIGHT);
useFunctionNamespaceLabel.setToolTipText(USE_NAMESPACE_TOOLTIP);
}
private void createNonCommentMarkupChoices() {
@@ -485,19 +464,13 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
functionSignatureComboBox = createComboBox(VTOptionDefines.FUNCTION_SIGNATURE,
DEFAULT_OPTION_FOR_FUNCTION_SIGNATURE);
functionSignatureComboBox.setToolTipText(FUNCTION_SIGNATURE_TOOLTIP);
}
private void setupNonCommentMarkupChoiceListeners() {
dataMatchDataTypeComboBox.addActionListener(defaultActionListener);
labelsComboBox.addActionListener(defaultActionListener);
functionNameComboBox.addActionListener(defaultActionListener);
functionSignatureComboBox.addActionListener(defaultActionListener);
useFunctionNamespaceCheckBox = createCheckBox("");
}
private JPanel createCommentsSubPanel() {
createCommentLabels();
createCommentChoices();
setupCommentChoiceListeners();
JPanel outerPanel = new JPanel();
outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
@@ -562,14 +535,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
postCommentsComboBox.setToolTipText(POST_COMMENT_TOOLTIP);
}
private void setupCommentChoiceListeners() {
plateCommentsComboBox.addActionListener(defaultActionListener);
preCommentsComboBox.addActionListener(defaultActionListener);
endOfLineCommentsComboBox.addActionListener(defaultActionListener);
repeatableCommentsComboBox.addActionListener(defaultActionListener);
postCommentsComboBox.addActionListener(defaultActionListener);
}
private void updateOptions(ToolOptions options) {
updateNonCommentMarkupOptions(options);
updateCommentOptions(options);
@@ -594,6 +559,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
FunctionSignatureChoices functionSignatureChoice =
(FunctionSignatureChoices) functionSignatureComboBox.getSelectedItem();
options.setEnum(FUNCTION_SIGNATURE, functionSignatureChoice);
boolean useNamespaces = useFunctionNamespaceCheckBox.isSelected();
options.setBoolean(USE_NAMESPACE_FUNCTIONS, useNamespaces);
}
private void updateCommentOptions(ToolOptions options) {
@@ -704,6 +672,12 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
if (functionSignatureChoice != functionSignatureComboBox.getSelectedItem()) {
functionSignatureComboBox.setSelectedItem(functionSignatureChoice);
}
boolean useNamespace =
options.getBoolean(USE_NAMESPACE_FUNCTIONS, DEFAULT_OPTION_FOR_NAMESPACE_FUNCTIONS);
if (useFunctionNamespaceCheckBox.isSelected() != useNamespace) {
useFunctionNamespaceCheckBox.setSelected(useNamespace);
}
}
private void setEditorCommentValues(ToolOptions options) {
@@ -852,7 +826,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
// signals that there are unapplied changes
private void changesMade(boolean changes) {
// FIXME Is this ok? complete?
if (listener != null) {
listener.propertyChange(new PropertyChangeEvent(this, GhidraOptions.APPLY_ENABLED,
unappliedChanges, changes));
@@ -766,6 +766,9 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
"Markup items that are incomplete (for example, no destination address is specified) " +
"should become ignored by applying a match.");
vtOptions.registerOption(USE_NAMESPACE_FUNCTIONS, DEFAULT_OPTION_FOR_NAMESPACE_FUNCTIONS,
null, "Apply the non-Global source namespace to the destination function.");
vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME)
.registerOptionsEditor(() -> new ApplyMarkupPropertyEditor(controller));
vtOptions.getOptions(DISPLAY_APPLY_MARKUP_OPTIONS)
@@ -68,6 +68,8 @@ public class VTOptionDefines {
public static CommentChoices DEFAULT_OPTION_FOR_POST_COMMENTS =
CommentChoices.APPEND_TO_EXISTING;
public static boolean DEFAULT_OPTION_FOR_NAMESPACE_FUNCTIONS = false;
public static final String FUNCTION_NAME = APPLY_MARKUP_OPTIONS_NAME + ".Function Name";
public static final String FUNCTION_RETURN_TYPE = APPLY_MARKUP_OPTIONS_NAME +
".Function Return Type";
@@ -105,9 +107,12 @@ public class VTOptionDefines {
public static final String IGNORE_EXCLUDED_MARKUP_ITEMS = APPLY_MARKUP_OPTIONS_NAME +
".Set Excluded Markup Items To Ignored";
public final static String DISPLAY_APPLY_MARKUP_OPTIONS = APPLY_MARKUP_OPTIONS_NAME +
public static final String DISPLAY_APPLY_MARKUP_OPTIONS = APPLY_MARKUP_OPTIONS_NAME +
Options.DELIMITER + "Display Apply Markup Options";
public static final String USE_NAMESPACE_FUNCTIONS =
APPLY_MARKUP_OPTIONS_NAME + ".Replace Namespace";
// Auto VT Options
public static final String AUTO_VT_OPTIONS_NAME = "Auto Version Tracking Options";
@@ -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,6 +35,7 @@ import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.test.*;
import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedIntegrationTest {
@@ -55,6 +56,10 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
env = new TestEnv();
setErrorGUIEnabled(false);
resetPrograms();
}
protected void resetPrograms() throws Exception {
sourceBuilder = new ClassicSampleX86ProgramBuilder("notepadSrc", true, this);
sourceProgram = sourceBuilder.getProgram();
@@ -112,6 +117,8 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
*/
protected abstract class TestDataProviderAndValidator {
protected ToolOptions options;
protected abstract Address getSourceMatchAddress();
protected abstract Address getDestinationMatchAddress();
@@ -134,6 +141,11 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
* @return the Apply Markup Options for the validator to use.
*/
protected ToolOptions getOptions() {
if (options != null) {
return options;
}
ToolOptions applyOptions = new ToolOptions(VERSION_TRACKING_OPTIONS_NAME);
applyOptions.setEnum(FUNCTION_NAME, FunctionNameChoices.REPLACE_ALWAYS);
@@ -165,10 +177,29 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
applyOptions.setEnum(DATA_MATCH_DATA_TYPE,
ReplaceDataChoices.REPLACE_UNDEFINED_DATA_ONLY);
options = applyOptions;
return applyOptions;
}
}
protected void doTestFindButCannotApplyMarkupItem_SameStatus(
TestDataProviderAndValidator validator) throws Exception {
VTSessionDB session = createNewSession();
VTMatch match = createMatchSetWithOneMatch(session, validator.getSourceMatchAddress(),
validator.getDestinationMatchAddress());
VTMarkupItem markupItem = validator.searchForMarkupItem(match);
List<VTMarkupItem> markupItems = new ArrayList<>();
Address destinationApplyAddress = validator.getDestinationApplyAddress();
markupItem.setDefaultDestinationAddress(destinationApplyAddress, TEST_ADDRESS_SOURCE);
markupItems.add(markupItem);
VTMarkupItemStatus status = markupItem.getStatus();
assertEquals(VTMarkupItemStatus.SAME, status);
}
protected void doTestFindAndApplyMarkupItem(TestDataProviderAndValidator validator)
throws Exception {
@@ -319,7 +350,10 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
// verify that calling apply still leaves us in an unapplied state
//
task = new ApplyMarkupItemTask(session, markupItems, validator.getOptions());
setErrorsExpected(true);
runTask(session, task);
setErrorsExpected(false);
assertEquals("The markup item status was not correctly set",
VTMarkupItemStatus.FAILED_APPLY, markupItem.getStatus());
@@ -354,9 +388,8 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
protected static VTMatch createMatchSetWithOneMatch(VTSessionDB db, Address sourceAddress,
Address destinationAddress) throws Exception {
int testTransactionID = 0;
try {
testTransactionID = db.startTransaction("Test Match Set Setup");
return tx(db, () -> {
VTMatchInfo matchInfo = createRandomMatch(sourceAddress, destinationAddress, db);
VTMatchSet matchSet = db.createMatchSet(
createProgramCorrelator(db.getSourceProgram(), db.getDestinationProgram()));
@@ -368,19 +401,22 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
association.getMarkupItems(TaskMonitor.DUMMY);
return addedMatch;
}
finally {
db.endTransaction(testTransactionID, true);
}
});
}
private void runTask(VTSession session, VtTask task) {
protected void runTask(VTSession session, VtTask task) {
int id = session.startTransaction("test");
try {
task.run(TaskMonitor.DUMMY);
}
finally {
session.endTransaction(id, true);
if (task.hasErrors()) {
String errorDetails = task.getErrorDetails();
Msg.error(this, "Error applying task: " + errorDetails);
}
}
}
@@ -96,6 +96,7 @@ public class VersionControlScreenShots extends GhidraScreenShotGenerator {
UndoActionDialog d = waitForDialogComponent(UndoActionDialog.class);
captureDialog(d);
close(d);
}