diff --git a/Ghidra/Features/Base/ghidra_scripts/BuildGhidraJarScript.java b/Ghidra/Features/Base/ghidra_scripts/BuildGhidraJarScript.java
index b6a3f8e809..675904cb82 100644
--- a/Ghidra/Features/Base/ghidra_scripts/BuildGhidraJarScript.java
+++ b/Ghidra/Features/Base/ghidra_scripts/BuildGhidraJarScript.java
@@ -17,10 +17,9 @@
//@category Examples
import java.io.File;
-import java.util.*;
+import java.util.List;
import generic.jar.ApplicationModule;
-import generic.jar.ResourceFile;
import ghidra.app.script.GhidraScript;
import ghidra.framework.Application;
import ghidra.util.GhidraJarBuilder;
@@ -32,8 +31,7 @@ public class BuildGhidraJarScript extends GhidraScript {
@Override
public void run() throws Exception {
- GhidraJarBuilder builder =
- new GhidraJarBuilder(toFiles(Application.getApplicationRootDirectories()));
+ GhidraJarBuilder builder = new GhidraJarBuilder(Application.getApplicationLayout());
builder.setMainClass("ghidra.JarRun"); // default is ghidra.JarRun, only here if you want
// to change it to something else.
@@ -69,12 +67,4 @@ public class BuildGhidraJarScript extends GhidraScript {
// uncomment the following line to create a src zip for debugging.
// builder.buildSrcZip(new File(installDir, "GhidraSrc.zip"), monitor);
}
-
- private List toFiles(Collection resourceFiles) {
- List fileList = new ArrayList<>();
- for (ResourceFile resourceFile : resourceFiles) {
- fileList.add(resourceFile.getFile(true));
- }
- return fileList;
- }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java
index eb3e5a4c09..216a85bbd6 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/clipboard/CodeBrowserClipboardProvider.java
@@ -24,6 +24,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
+import org.apache.commons.lang3.StringUtils;
+
import docking.ActionContext;
import docking.ComponentProvider;
import docking.dnd.GenericDataFlavor;
@@ -303,12 +305,9 @@ public class CodeBrowserClipboardProvider extends ByteCopier
private Transferable copyAddress() {
AddressSetView addressSet = getSelectedAddresses();
- StringBuilder buffy = new StringBuilder();
AddressIterator it = addressSet.getAddresses(true);
- while (it.hasNext()) {
- buffy.append(it.next()).append('\n');
- }
- return createStringTransferable(buffy.toString());
+ String joined = StringUtils.join((Iterator) it, "\n");
+ return createStringTransferable(joined);
}
protected Transferable copyCode(TaskMonitor monitor) {
@@ -377,8 +376,8 @@ public class CodeBrowserClipboardProvider extends ByteCopier
private boolean pasteLabelsComments(Transferable pasteData, boolean pasteLabels,
boolean pasteComments) {
try {
- List> list = (List>) pasteData.getTransferData(
- CodeUnitInfoTransferable.localDataTypeFlavor);
+ List> list =
+ (List>) pasteData.getTransferData(CodeUnitInfoTransferable.localDataTypeFlavor);
List infos = CollectionUtils.asList(list, CodeUnitInfo.class);
Command cmd = new CodeUnitInfoPasteCmd(currentLocation.getAddress(), infos, pasteLabels,
pasteComments);
@@ -420,7 +419,7 @@ public class CodeBrowserClipboardProvider extends ByteCopier
return pasteOperandField((OperandFieldLocation) currentLocation, labelName);
}
- // try pasting onto something that is not a label
+ // try pasting onto something that is not a label
return maybePasteNonLabelString(labelName);
}
@@ -451,12 +450,12 @@ public class CodeBrowserClipboardProvider extends ByteCopier
String oldName = symbol.getName();
Namespace namespace = symbol.getParentNamespace();
Address symbolAddress = symbol.getAddress();
- RenameLabelCmd cmd = new RenameLabelCmd(symbolAddress, oldName, labelName,
- namespace, SourceType.USER_DEFINED);
+ RenameLabelCmd cmd = new RenameLabelCmd(symbolAddress, oldName, labelName, namespace,
+ SourceType.USER_DEFINED);
return tool.execute(cmd, currentProgram);
}
- // try pasting onto something that is not a label
+ // try pasting onto something that is not a label
return maybePasteNonLabelString(labelName);
}
@@ -646,7 +645,7 @@ public class CodeBrowserClipboardProvider extends ByteCopier
//==================================================================================================
// Unsupported Operations
-//==================================================================================================
+//==================================================================================================
@Override
public void lostOwnership(Transferable transferable) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java
index b55dffb890..03b7d601bb 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java
@@ -441,6 +441,7 @@ public abstract class DemangledObject implements Demangled {
namespace = program.getGlobalNamespace();
}
+ SymbolTable symbolTable = program.getSymbolTable();
for (String namespaceName : getNamespaceList(typeNamespace)) {
// TODO - This is compensating for too long templates. We should probably genericize
@@ -448,45 +449,43 @@ public abstract class DemangledObject implements Demangled {
// same name is the same class--would that reflect reality?
namespaceName = ensureNameLength(namespaceName);
- SymbolTable symbolTable = program.getSymbolTable();
-
- List symbols = symbolTable.getSymbols(namespaceName, namespace);
- Symbol namespaceSymbol =
- symbols.stream().filter(s -> (s.getSymbolType() == SymbolType.NAMESPACE ||
- s.getSymbolType() == SymbolType.CLASS)).findFirst().orElse(null);
- if (namespaceSymbol == null) {
- try {
- namespace =
- symbolTable.createNameSpace(namespace, namespaceName, SourceType.IMPORTED);
- }
- catch (DuplicateNameException e) {
- Msg.error(DemangledObject.class,
- "Failed to create namespace due to name conflict: " +
- NamespaceUtils.getNamespaceQualifiedName(namespace, namespaceName,
- false));
- break;
- }
- catch (InvalidInputException e) {
- Msg.error(DemangledObject.class,
- "Failed to create namespace: " + e.getMessage());
- break;
- }
+ try {
+ namespace =
+ symbolTable.getOrCreateNameSpace(namespace, namespaceName, SourceType.IMPORTED);
}
- else if (isPermittedNamespaceSymbol(namespaceSymbol, functionPermitted)) {
- namespace = (Namespace) namespaceSymbol.getObject();
- }
- else {
+ catch (DuplicateNameException e) {
Msg.error(DemangledObject.class,
"Failed to create namespace due to name conflict: " +
+ NamespaceUtils.getNamespaceQualifiedName(namespace, namespaceName,
+ false));
+ break;
+ }
+ catch (InvalidInputException e) {
+ Msg.error(DemangledObject.class,
+ "Failed to create namespace: " + e.getMessage());
+ break;
+ }
+
+ Symbol nsSymbol = namespace.getSymbol();
+ if (!isPermittedNamespaceType(nsSymbol.getSymbolType(), functionPermitted)) {
+
+ String allowedTypes = "SymbolType.CLASS, SymbolType.NAMESPACE";
+ if (functionPermitted) {
+ allowedTypes += ", SymbolType.FUNCTION";
+ }
+
+ Msg.error(DemangledObject.class,
+ "Bad namespace type - must be one of: " + allowedTypes +
NamespaceUtils.getNamespaceQualifiedName(namespace, namespaceName, false));
break;
}
+
}
return namespace;
}
- private static boolean isPermittedNamespaceSymbol(Symbol symbol, boolean functionPermitted) {
- SymbolType symbolType = symbol.getSymbolType();
+ private static boolean isPermittedNamespaceType(SymbolType symbolType,
+ boolean functionPermitted) {
if (symbolType == SymbolType.CLASS || symbolType == SymbolType.NAMESPACE) {
return true;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/VarnodeContext.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/VarnodeContext.java
index 760bf5fd60..e0d1e22cc1 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/VarnodeContext.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/VarnodeContext.java
@@ -34,8 +34,7 @@ import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.util.Msg;
-import ghidra.util.exception.DuplicateNameException;
-import ghidra.util.exception.NotFoundException;
+import ghidra.util.exception.*;
public class VarnodeContext implements ProcessorContext {
@@ -88,7 +87,7 @@ public class VarnodeContext implements ProcessorContext {
this.program = program;
// make a copy, because we could be making new spaces.
- this.addrFactory = new OffsetAddressFactory(program.getAddressFactory());
+ this.addrFactory = new OffsetAddressFactory(program);
BAD_ADDRESS = addrFactory.getAddress(getAddressSpace("BAD_ADDRESS_SPACE"), 0);
@@ -1435,8 +1434,24 @@ public class VarnodeContext implements ProcessorContext {
class OffsetAddressFactory extends DefaultAddressFactory {
- OffsetAddressFactory(AddressFactory baseFactory) {
- super(filterSpaces(baseFactory.getAllAddressSpaces()));
+ OffsetAddressFactory(Program program) {
+ // We are only calling super with the address spaces from the language first, and then
+ // following up to explicitly add more spaces due to the treatment of memory address
+ // spaces by DefaultAddressFactory when constructed vs. when added later.
+ // If there is more than one memory address space (e.g., TYPE_RAM, TYPE_CODE, or
+ // TYPE_OTHER), then addresses are output with the space name prefix, which we do not want.
+ super(program.getLanguage().getAddressFactory().getAllAddressSpaces(),
+ program.getLanguage().getAddressFactory().getDefaultAddressSpace());
+ for (AddressSpace space : program.getAddressFactory().getAllAddressSpaces()) {
+ if (space.isLoadedMemorySpace() && getAddressSpace(space.getName()) == null) {
+ try {
+ addAddressSpace(space);
+ }
+ catch (DuplicateNameException e) {
+ throw new AssertException("Duplicate name should not occur.");
+ }
+ }
+ }
}
private int getNextUniqueID() {
@@ -1466,17 +1481,4 @@ class OffsetAddressFactory extends DefaultAddressFactory {
return (type == AddressSpace.TYPE_SYMBOL);
}
- private static AddressSpace[] filterSpaces(AddressSpace[] allSpaces) {
- List spaces = new ArrayList<>();
- for (AddressSpace space : allSpaces) {
- int type = space.getType();
- if (type == AddressSpace.TYPE_VARIABLE || type == AddressSpace.TYPE_STACK ||
- type == AddressSpace.TYPE_EXTERNAL || type == AddressSpace.TYPE_JOIN) {
- continue;
- }
- spaces.add(space);
- }
- return spaces.toArray(new AddressSpace[0]);
- }
-
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/GhidraJarBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/util/GhidraJarBuilder.java
index 7ded06d9a2..5dd1622017 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/util/GhidraJarBuilder.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/util/GhidraJarBuilder.java
@@ -25,28 +25,24 @@ import java.util.zip.*;
import generic.jar.*;
import ghidra.GhidraApplicationLayout;
import ghidra.GhidraLaunchable;
-import ghidra.framework.Application;
-import ghidra.framework.HeadlessGhidraApplicationConfiguration;
+import ghidra.framework.*;
import ghidra.framework.plugintool.dialog.ExtensionUtils;
import ghidra.util.classfinder.ClassFinder;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
-import ghidra.util.task.TaskMonitorAdapter;
import utilities.util.FileUtilities;
+import utility.application.ApplicationLayout;
import utility.module.ModuleUtilities;
public class GhidraJarBuilder implements GhidraLaunchable {
private static final String ROOT = "_Root/";
private static final String ROOT_GHIDRA = "_Root/Ghidra/";
- private static final String LIBS_FILE_MODULE_KEY = "Module: ";
// this is set in the buildGhidraJar batch/script files
- private static final String GHIDRA_DIR = "Ghidra.Install.Root.Dir";
private static final String INVOCATION_NAME_PROPERTY = "GhidraJarBuilder.Name";
- private static HashMap> libsMap = new HashMap<>();
private List rootGhidraDirs = new ArrayList<>();
private List allModules;
private Set includedModules = new HashSet<>();
@@ -57,19 +53,18 @@ public class GhidraJarBuilder implements GhidraLaunchable {
private Pattern extensionPointSuffixPattern;
private List extensionPointClasses = new ArrayList<>();
private ClassLoader classLoader;
- private boolean inGradleMode = false;
private Set processedJars = new HashSet<>();
public GhidraJarBuilder() {
// Required for GhidraLaunchable
}
- public GhidraJarBuilder(List rootDirs) throws IOException {
- for (File file : rootDirs) {
- File rgd = file.getCanonicalFile();
+ public GhidraJarBuilder(ApplicationLayout layout) throws IOException {
+ for (ResourceFile file : layout.getApplicationRootDirs()) {
+ File rgd = file.getFile(false).getCanonicalFile();
rootGhidraDirs.add(rgd);
}
- allModules = findAllModules();
+ allModules = findAllModules(layout);
Collections.sort(allModules);
for (ApplicationModule module : allModules) {
if (includeByDefault(module)) {
@@ -203,7 +198,7 @@ public class GhidraJarBuilder implements GhidraLaunchable {
for (ApplicationModule module : moduleList) {
writeModuleClassesAndResources(jar, module);
- if (!excludeHelp && !inGradleMode) {
+ if (!excludeHelp) {
writeModuleHelp(jar, module);
}
}
@@ -360,16 +355,6 @@ public class GhidraJarBuilder implements GhidraLaunchable {
private void writeModuleClassesAndResources(Jar jar, ApplicationModule module)
throws CancelledException, IOException {
- if (inGradleMode) {
- File gradleBuildFileForModule =
- new File(module.getModuleDir(), "build/libs/" + module.getName() + ".jar");
- processJarFile(jar, gradleBuildFileForModule, module);
- File gradleBuildFileForGPLModule =
- new File(module.getModuleDir(), "build/data/lib/" + module.getName() + ".jar");
- processJarFile(jar, gradleBuildFileForGPLModule, module);
- processExternalLibs(jar, module);
- return;
- }
// NOTE: This only works in a distribution where the 3rd party jars live in each
// module's libs directory
File binDir = new File(module.getModuleDir(), "bin/main");
@@ -380,18 +365,6 @@ public class GhidraJarBuilder implements GhidraLaunchable {
processLibDir(jar, module);
}
- private void processExternalLibs(Jar jar, ApplicationModule module)
- throws CancelledException, IOException {
- List list = libsMap.get(module.getName());
- if (list == null) {
- return;
- }
- for (String libPath : list) {
- File file = new File(libPath);
- processJarFile(jar, file, module);
- }
- }
-
private void processLibDir(Jar jar, ApplicationModule module)
throws CancelledException, IOException {
File libDir = new File(module.getModuleDir(), "lib");
@@ -585,16 +558,34 @@ public class GhidraJarBuilder implements GhidraLaunchable {
}
return manifest;
}
-
- private List findAllModules() {
+
+ private List findAllModules(ApplicationLayout layout) throws IOException {
List modules = new ArrayList<>();
- for (File appRoot : rootGhidraDirs) {
- findModules(appRoot, appRoot, modules);
- findModules(appRoot, new File(appRoot, "../GPL"), modules);
-
+
+ for (GModule module : layout.getModules().values()) {
+ File moduleDir = module.getModuleRoot().getFile(false).getCanonicalFile();
+ File rootDir = getModuleRootDir(moduleDir);
+ modules.add(new ApplicationModule(rootDir, moduleDir));
}
+
return modules;
}
+
+ private File getModuleRootDir(File moduleDir) {
+ // Look in GPL directories too
+ List rootDirs = new ArrayList<>(rootGhidraDirs);
+ for (File rootDir : rootGhidraDirs) {
+ rootDirs.add(new File(rootDir.getParentFile(), "GPL"));
+ }
+
+ // Check each root directory to see if it contains the module
+ for (File rootDir : rootDirs) {
+ if (FileUtilities.isPathContainedWithin(rootDir, moduleDir)) {
+ return rootDir;
+ }
+ }
+ throw new AssertException("Module root directory could not be determined: " + moduleDir);
+ }
private String getPathFromRoot(String rootPath, File file) {
String filePath = file.getAbsolutePath();
@@ -604,23 +595,6 @@ public class GhidraJarBuilder implements GhidraLaunchable {
return filePath.substring(rootPath.length() + 1);
}
- private void findModules(File rootAppDir, File dir, List modules) {
- File moduleManifest = new File(dir, "Module.manifest");
- if (moduleManifest.exists()) {
- ApplicationModule module = new ApplicationModule(rootAppDir, dir);
- modules.add(module);
- return; // modules can't live in other modules;
- }
- File[] listFiles = dir.listFiles();
- if (listFiles != null) {
- for (File file : listFiles) {
- if (file.isDirectory()) {
- findModules(rootAppDir, file, modules);
- }
- }
- }
- }
-
private void checkExtensionPointClass(String path, InputStream inputStream) {
// remove .class
path = path.substring(0, path.length() - 6);
@@ -931,44 +905,15 @@ public class GhidraJarBuilder implements GhidraLaunchable {
}
}
- private static void parseLibsFile(String libsFilePath) {
- try {
- List lines = FileUtilities.getLines(new File(libsFilePath));
-
- List libPaths = new ArrayList<>();
- String currentModule = null;
- for (String line : lines) {
- if (line.startsWith(LIBS_FILE_MODULE_KEY)) {
- if (currentModule != null) {
- libsMap.put(currentModule, libPaths);
- libPaths = new ArrayList<>();
- }
- currentModule = line.substring(LIBS_FILE_MODULE_KEY.length()).trim();
- }
- else {
- libPaths.add(line.trim());
- }
- }
- }
- catch (IOException e) {
- System.err.println("Could not read lib paths file: " + libsFilePath);
- System.exit(0);
- }
- }
-
private static void usage(String[] args) {
for (int i = 0; i < args.length; i++) {
System.err.println("arg " + i + ": " + args[i]);
}
String invocationName = System.getProperty(INVOCATION_NAME_PROPERTY);
- String property = System.getProperty(GHIDRA_DIR);
StringBuffer buf = new StringBuffer();
buf.append("\nUsage: ");
- buf.append(invocationName != null ? invocationName : "GhidraJarBuilder ");
- if (property == null) {
- buf.append(" [ ...] ");
- }
+ buf.append(invocationName != null ? invocationName : "GhidraJarBuilder");
buf.append(
" [-output
+ The View Settings option describes how the graph will be zoomed when it is first
+ loaded. The values are:
+
+
+ - Start Fully Zoomed Out - always start fully zoomed out so that the entire
+ graph can be seen.
+
+ - Start Fully Zoomed In/B> - always start fully zoomed in on the vertex containing
+ the current location.
+
+ - Remember User Settings - keep the zoom level where the user previously left
+ it.
+
+
+
+
There are various edge color and highlight color options available to change. The
highlight colors are those to be used when the flow animations take place.
diff --git a/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph_Layouts.html b/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph_Layouts.html
index 2f2eab68a8..6e71260598 100644
--- a/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph_Layouts.html
+++ b/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph_Layouts.html
@@ -49,6 +49,13 @@
notes on how edges are routed for this layout.)
+
+
+ The Use Dim Return Edges option makes default code block return flow edges
+ lighter than conditional edges. This makes it easier for users to scan the
+ graph and ignore return flows.
+
+
diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java
index 51b6773a9a..ea22f1cf44 100644
--- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java
+++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java
@@ -42,7 +42,7 @@ public class FunctionGraphOptions extends VisualGraphOptions {
private static final String EDGE_COLOR_CONDITIONAL_JUMP_KEY = "Edge Color - Conditional Jump ";
//@formatter:off
- private static final String NAVIGATION_HISTORY_KEY = "Navigation History";
+ private static final String NAVIGATION_HISTORY_KEY = "Navigation History";
private static final String NAVIGATION_HISTORY_DESCRIPTION =
"Determines how the navigation history will be updated when using the Function Graph. " +
"The basic options are:" +
@@ -185,8 +185,8 @@ public class FunctionGraphOptions extends VisualGraphOptions {
options.registerOption(SCROLL_WHEEL_PANS_KEY, getScrollWheelPans(), help,
SCROLL_WHEEL_PANS_DESCRIPTION);
- options.registerOption(GRAPH_BACKGROUND_COLOR_KEY, DEFAULT_GRAPH_BACKGROUND_COLOR,
- help, GRAPH_BACKGROUND_COLOR_DESCRPTION);
+ options.registerOption(GRAPH_BACKGROUND_COLOR_KEY, DEFAULT_GRAPH_BACKGROUND_COLOR, help,
+ GRAPH_BACKGROUND_COLOR_DESCRPTION);
options.registerOption(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, DEFAULT_VERTEX_BACKGROUND_COLOR,
help, DEFAULT_VERTEX_BACKGROUND_COLOR_DESCRPTION);
diff --git a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLayoutOptions.java b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLayoutOptions.java
index 1467a84781..1549c2cd9a 100644
--- a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLayoutOptions.java
+++ b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DNLayoutOptions.java
@@ -31,7 +31,12 @@ public class DNLayoutOptions implements FGLayoutOptions {
"edges should be routed around any intersecting vertex. When toggled off, edges will " +
"pass through any intersecting vertices.";
+ private static final String DIM_RETURN_EDGES_KEY = "Use Dim Return Edges";
+ private static final String DIM_RETURN_EDGES_DESCRIPTION =
+ "Signals to lighten the default return edges.";
+
private boolean useEdgeRoutingAroundVertices;
+ private boolean useDimmedReturnEdges = true;
@Override
public void registerOptions(Options options) {
@@ -40,21 +45,32 @@ public class DNLayoutOptions implements FGLayoutOptions {
options.registerOption(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices,
help, USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION);
+
+ options.registerOption(DIM_RETURN_EDGES_KEY, useDimmedReturnEdges, help,
+ DIM_RETURN_EDGES_DESCRIPTION);
}
@Override
public void loadOptions(Options options) {
useEdgeRoutingAroundVertices =
options.getBoolean(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices);
+
+ useDimmedReturnEdges = options.getBoolean(DIM_RETURN_EDGES_KEY, useDimmedReturnEdges);
+
}
public boolean useEdgeRoutingAroundVertices() {
return useEdgeRoutingAroundVertices;
}
+ public boolean useDimmedReturnEdges() {
+ return useDimmedReturnEdges;
+ }
+
@Override
public boolean optionChangeRequiresRelayout(String optionName) {
// format: 'Nested Code Layout.Route Edges....'
- return optionName.endsWith(USE_EDGE_ROUTING_AROUND_VERTICES_KEY);
+ return optionName.endsWith(USE_EDGE_ROUTING_AROUND_VERTICES_KEY) ||
+ optionName.endsWith(DIM_RETURN_EDGES_KEY);
}
}
diff --git a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java
index 761c18f743..39c2744d80 100644
--- a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java
+++ b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java
@@ -38,6 +38,7 @@ import ghidra.app.plugin.core.functiongraph.graph.jung.renderer.DNLArticulatedEd
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
import ghidra.app.plugin.core.functiongraph.graph.vertex.GroupedFunctionGraphVertex;
import ghidra.graph.VisualGraph;
+import ghidra.graph.viewer.GraphViewerUtils;
import ghidra.graph.viewer.layout.*;
import ghidra.graph.viewer.vertex.VisualGraphVertexShapeTransformer;
import ghidra.program.model.address.Address;
@@ -52,19 +53,19 @@ import ghidra.util.task.TaskMonitor;
/**
* A layout that uses the decompiler to show code nesting based upon conditional logic.
- *
+ *
* Edges returning to the default code flow are painted lighter to de-emphasize them. This
* could be made into an option.
- *
- *
Edge routing herein defaults to 'simple routing'; 'complex routing' is a user option.
+ *
+ *
Edge routing herein defaults to 'simple routing'; 'complex routing' is a user option.
* Simple routing will reduce edge noise as much as possible by combining/overlapping edges that
* flow towards the bottom of the function (returning code flow). Also, edges may fall behind
* vertices for some functions. Complex routing allows the user to visually follow the flow
* of an individual edge. Complex routing will prevent edges from overlapping and will route
- * edges around vertices. Simple routing is better when the layout of the vertices is
- * important to the user; complex routing is better when edges/relationships are more
+ * edges around vertices. Simple routing is better when the layout of the vertices is
+ * important to the user; complex routing is better when edges/relationships are more
* important to the user.
- *
+ *
* TODO ideas:
* -paint fallthrough differently for all, or just for those returning to the baseline
*/
@@ -113,7 +114,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
@Override
protected double getCondenseFactor() {
- // our layout needs more spacing because we have custom edge routing that we want to
+ // our layout needs more spacing because we have custom edge routing that we want to
// stand out
return .3;
}
@@ -201,7 +202,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
continue;
}
- //
+ //
// Special case: fallthrough--don't label this...not sure how to tell fallthrough. For
// now assume that any column below or backwards is fallthrough. However,
// do label fallthrough if it is the only edge.
@@ -231,10 +232,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
Map> newEdgeArticulations = new HashMap<>();
- // Condensing Note: we have guilty knowledge that our parent class my condense the
+ // Condensing Note: we have guilty knowledge that our parent class my condense the
// vertices and edges towards the center of the graph after we calculate positions.
// To prevent the edges from moving to far behind the vertices, we will compensate a
- // bit for that effect using this offset value. The getEdgeOffset() method below is
+ // bit for that effect using this offset value. The getEdgeOffset() method below is
// updated for the condense factor.
int edgeOffset = isCondensedLayout()
? (int) (VERTEX_TO_EDGE_ARTICULATION_PADDING * (1 - getCondenseFactor()))
@@ -242,7 +243,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
Vertex2dFactory vertex2dFactory =
new Vertex2dFactory(transformer, vertexLayoutLocations, layoutToGridMap, edgeOffset);
- //
+ //
// Route our edges!
//
for (FGEdge e : edges) {
@@ -262,11 +263,8 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
DecompilerBlock loop = block.getParentLoop();
if (loop != null) {
- Set vertices = loop.getVertices();
-
- Column outermostCol = getOutermostCol(layoutToGridMap, vertices);
- Column loopEndColumn = layoutToGridMap.nextColumn(outermostCol);
- List articulations = routeLoopEdge(start, end, loopEndColumn);
+ List articulations =
+ routeUpwardLoop(layoutToGridMap, vertex2dFactory, start, end, loop);
newEdgeArticulations.put(e, articulations);
continue;
}
@@ -275,31 +273,31 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
List articulations = new ArrayList<>();
//
- // Basic routing:
+ // Basic routing:
// -leave the bottom of the start vertex
// -first bend at some constant offset
// -move to right or left, to above the end vertex
// -second bend above the end vertex at previous constant offset
- //
- // Edges start/end on the vertex center. If we offset them to avoid
+ //
+ // Edges start/end on the vertex center. If we offset them to avoid
// overlapping, then they produce angles when only using two articulations.
// Thus, we create articulations that are behind the vertices to remove
// the angles. This points will not be seen.
//
- //
+ //
// Complex routing:
// -this mode will route edges around vertices
- //
+ //
// One goal for complex edge routing is to prevent overlapping (simple edge routing
// prefers overlapping to reduce lines). To prevent overlapping we will use different
- // offset x and y values, depending upon the start and end vertex row and column
+ // offset x and y values, depending upon the start and end vertex row and column
// locations. Specifically, for a given edge direction there will be a bias:
// -Edge to the right - leave from the right; arrive to the left
// -Edge to the left - leave from the left; arrive to the right
// -Edge straight down - go straight down
//
// For each of the above offsets, there will be an amplifier based upon row/column
- // distance from start to end vertex. This has the effect that larger vertex
+ // distance from start to end vertex. This has the effect that larger vertex
// distances will have a larger offset/spacing.
//
@@ -331,14 +329,78 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
return newEdgeArticulations;
}
+ private List routeUpwardLoop(LayoutLocationMap layoutToGridMap,
+ Vertex2dFactory vertex2dFactory, Vertex2d start, Vertex2d end, DecompilerBlock loop) {
+ Set loopVertices = loop.getVertices();
+ FGVertex rightmostLoopVertex =
+ getRightmostVertex(layoutToGridMap, vertex2dFactory, loopVertices);
+
+ int startRow = start.rowIndex;
+ int endRow = end.rowIndex;
+ int startColumn = Math.min(start.columnIndex, end.columnIndex);
+ int endColumn = Math.max(start.columnIndex, end.columnIndex);
+
+ Column rightmostLoopColumn = layoutToGridMap.col(rightmostLoopVertex);
+ endColumn = Math.max(endColumn, rightmostLoopColumn.index);
+
+ // Look for any vertices that are no part of the loop, but are placed inside
+ // of the loop bounds. This can happen in a graph when the decompiler uses
+ // goto statements. Use the loop's rightmost vertex to establish the loops
+ // right edge and then use that to check for any stray non-loop vertices.
+ List interlopers =
+ getVerticesInBounds(vertex2dFactory, startRow, endRow, startColumn, endColumn);
+
+ // place the right x position to the right of the rightmost vertex, not
+ // extending past the next column
+ FGVertex rightmostVertex = getRightmostVertex(interlopers);
+ Column rightmostColumn = layoutToGridMap.col(rightmostVertex);
+ Column nextColumn = layoutToGridMap.nextColumn(rightmostColumn);
+ Vertex2d rightmostV2d = vertex2dFactory.get(rightmostVertex);
+
+ // the padding used for these two lines is somewhat arbitrary and may be changed
+ double rightSide = rightmostV2d.getRight() + GraphViewerUtils.EXTRA_LAYOUT_COLUMN_SPACING;
+ double x = Math.min(rightSide,
+ nextColumn.x - GraphViewerUtils.EXTRA_LAYOUT_COLUMN_SPACING_CONDENSED);
+
+ List articulations = routeLoopEdge(start, end, x);
+ return articulations;
+ }
+
+ private List getVerticesInBounds(Vertex2dFactory vertex2dFactory, int startRow,
+ int endRow, int startColumn, int endColumn) {
+
+ if (startRow > endRow) { // going upwards
+ int temp = endRow;
+ endRow = startRow;
+ startRow = temp;
+ }
+
+ List toCheck = new LinkedList<>();
+ for (int row = startRow; row < endRow + 1; row++) {
+
+ for (int col = startColumn; col < endColumn + 1; col++) {
+
+ // assume any other vertex in our column can clip (it will not clip when
+ // the 'spacing' above pushes the edge away from this column, like for
+ // large row delta values)
+ Vertex2d otherVertex = vertex2dFactory.get(row, col);
+ if (otherVertex != null) {
+ toCheck.add(otherVertex);
+ }
+ }
+ }
+
+ return toCheck;
+ }
+
private void routeToTheRightGoingUpwards(Vertex2d start, Vertex2d end,
Vertex2dFactory vertex2dFactory, List articulations) {
//
- // For routing to the right and back up we will leave the start vertex from the right side
+ // For routing to the right and back up we will leave the start vertex from the right side
// and enter the end vertex on the right side. As the vertices get further apart, we will
- // space them further in towards the center.
- //
+ // space them further in towards the center.
+ //
int delta = start.rowIndex - end.rowIndex;
int multiplier = EDGE_ENDPOINT_DISTANCE_MULTIPLIER;
@@ -347,11 +409,16 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
}
int distanceSpacing = delta * multiplier;
- // Condensing Note: we have guilty knowledge that our parent class my condense the
+ // Condensing is when the graph will pull nodes closer together on the x axis to
+ // reduce whitespace and make the entire graph easier to see. In this case, update
+ // the offset to avoid running into the moved vertices.
+
+ // Condensing Note: we have guilty knowledge that our parent class my condense the
// vertices and edges towards the center of the graph after we calculate positions.
// To prevent the edges from moving to far behind the vertices, we will compensate a
- // bit for that effect using this offset value. The getEdgeOffset() method is
+ // bit for that effect using this offset value. The getEdgeOffset() method is
// updated for the condense factor.
+
int exaggerationFactor = 1;
if (isCondensedLayout()) {
exaggerationFactor = 2; // determined by trial-and-error; can be made into an option
@@ -369,7 +436,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
y1 = Math.min(y1, startCenterY);
articulations.add(new Point2D.Double(x1, y1)); // point is hidden behind the vertex
- // Use the spacing to move the y value towards the top of the vertex. Just like with
+ // Use the spacing to move the y value towards the top of the vertex. Just like with
// the x value, restrict the y to the range between the edge and the center.
double startRightX = start.getRight();
double x2 = startRightX + VERTEX_BORDER_THICKNESS; // start at the end
@@ -434,10 +501,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
//
// For routing to the left we will leave the start vertex from just left of center and
- // enter the end vertex on the top, towards the right. As the vertices get further apart,
- // we will space them further in towards the center of the end vertex. This will keep
+ // enter the end vertex on the top, towards the right. As the vertices get further apart,
+ // we will space them further in towards the center of the end vertex. This will keep
// edges with close endpoints from intersecting edges with distant endpoints.
- //
+ //
int delta = end.rowIndex - start.rowIndex;
int multiplier = EDGE_ENDPOINT_DISTANCE_MULTIPLIER;
@@ -499,7 +566,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// enter the end vertex on the left side. As the vertices get further apart, we will
// space them further in towards the center. This will keep edges with close endpoints
// from intersecting edges with distant endpoints.
- //
+ //
int delta = end.rowIndex - start.rowIndex;
if (delta < 0) {
@@ -529,7 +596,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
double y1 = start.getY();
articulations.add(new Point2D.Double(x1, y1)); // point is hidden behind the vertex
- // Use the spacing to move the y value towards the top of the vertex. Just like with
+ // Use the spacing to move the y value towards the top of the vertex. Just like with
// the x value, restrict the y to the range between the edge and the center.
double x2 = x1;
double y2 = end.getTop() + VERTEX_BORDER_THICKNESS;
@@ -556,19 +623,6 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
private void routeAroundColumnVertices(Vertex2d start, Vertex2d end,
Vertex2dFactory vertex2dFactory, List articulations, double edgeX) {
- Column column = vertex2dFactory.getColumn(edgeX);
- int columnIndex = 0;
- if (column != null) {
- // a null column happens with a negative x value that is outside of any column
- columnIndex = column.index;
- }
-
- routeAroundColumnVertices(start, end, columnIndex, vertex2dFactory, articulations, edgeX);
- }
-
- private void routeAroundColumnVertices(Vertex2d start, Vertex2d end, int column,
- Vertex2dFactory vertex2dFactory, List articulations, double edgeX) {
-
if (useSimpleRouting()) {
return;
}
@@ -582,14 +636,34 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
startRow = end.rowIndex;
}
+ int startColumn = Math.min(start.columnIndex, end.columnIndex);
+ int endColumn = Math.max(start.columnIndex, end.columnIndex);
+ if (goingDown) {
+ endRow -= 1;
+ endColumn -= 1;
+
+ if (start.columnIndex <= end.columnIndex) {
+ startRow += 1;
+ }
+ }
+ else {
+ // going up we swing out to the right; grab the column that is out to the right
+ Column rightColumn = vertex2dFactory.getColumn(edgeX);
+ endColumn = rightColumn.index;
+ }
+
List toCheck = new LinkedList<>();
- for (int row = startRow + 1; row < endRow; row++) {
- // assume any other vertex in our column can clip (it will not clip when
- // the 'spacing' above pushes the edge away from this column, like for
- // large row delta values)
- Vertex2d otherVertex = vertex2dFactory.get(row, column);
- if (otherVertex != null) {
- toCheck.add(otherVertex);
+ for (int row = startRow; row < endRow + 1; row++) {
+
+ for (int col = startColumn; col < endColumn + 1; col++) {
+
+ // assume any other vertex in our column can clip (it will not clip when
+ // the 'spacing' above pushes the edge away from this column, like for
+ // large row delta values)
+ Vertex2d otherVertex = vertex2dFactory.get(row, col);
+ if (otherVertex != null) {
+ toCheck.add(otherVertex);
+ }
}
}
@@ -605,11 +679,16 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
int padding = VERTEX_TO_EDGE_AVOIDANCE_PADDING;
int distanceSpacing = padding + delta; // adding the delta makes overlap less likely
- // Condensing Note: we have guilty knowledge that our parent class my condense the
+ // Condensing is when the graph will pull nodes closer together on the x axis to
+ // reduce whitespace and make the entire graph easier to see. In this case, update
+ // the offset to avoid running into the moved vertices.
+
+ // Condensing Note: we have guilty knowledge that our parent class my condense the
// vertices and edges towards the center of the graph after we calculate positions.
// To prevent the edges from moving to far behind the vertices, we will compensate a
- // bit for that effect using this offset value. The getEdgeOffset() method is
+ // bit for that effect using this offset value. The getEdgeOffset() method is
// updated for the condense factor.
+
int vertexToEdgeOffset = otherVertex.getEdgeOffset();
int exaggerationFactor = 1;
if (isCondensedLayout()) {
@@ -629,20 +708,20 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// no need to check the 'y' value, as the end vertex is above/below this one
if (vertexClipper.isClippingX(otherVertex, edgeX)) {
- /*
+ /*
Must route around this vertex - new points:
-p1 - just above the intersection point
-p2 - just past the left edge
-p3 - just past the bottom of the vertex
-p4 - back at the original x value
-
+
|
.___|
| .-----.
| | |
| '-----'
'---.
- |
+ |
*/
// p1 - same x; y just above vertex
@@ -650,19 +729,19 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
double y = vertexClipper.getTopOffset(otherVertex, vertexToEdgeOffset);
articulations.add(new Point2D.Double(x, y));
- // Maybe merge points if they are too close together. Visually, many lines
- // moving around intersecting vertices looks busy. When the intersecting
+ // Maybe merge points if they are too close together. Visually, many lines
+ // moving around intersecting vertices looks busy. When the intersecting
// vertices are close together, we remove some of the articulations in order to
// smooth out the edges.
if (articulations.size() > 2) {
- /*
+ /*
The last articulation is the one added before this method was called, which
- lies just below the intersecting vertex. The articulation before that is
- the one that is the one that is sending the x value straight into the
+ lies just below the intersecting vertex. The articulation before that is
+ the one that is the one that is sending the x value straight into the
intersecting vertex. Delete that point as well so that the entire edge is
shifted to the outside of the intersecting vertex. This will get repeated
- for each vertex that is intersecting.
+ for each vertex that is intersecting.
*/
Point2D previousArticulation = articulations.get(articulations.size() - 2);
int closenessHeight = 50;
@@ -696,15 +775,11 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
return !getLayoutOptions().useEdgeRoutingAroundVertices();
}
- private List routeLoopEdge(Vertex2d start, Vertex2d end, Column loopEndColumn) {
+ private List routeLoopEdge(Vertex2d start, Vertex2d end, double x) {
// going backwards
List articulations = new ArrayList<>();
- // loop first point - same y coord as the vertex; x is the middle of the next col
- int halfWidth = loopEndColumn.getPaddedWidth(isCondensedLayout()) >> 1;
- double x = loopEndColumn.x + halfWidth; // middle of the column
-
int startRow = start.rowIndex;
int endRow = end.rowIndex;
if (startRow > endRow) { // going upwards
@@ -720,7 +795,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
Point2D first = new Point2D.Double(x, y1);
articulations.add(first);
- // loop second point - same y coord as destination;
+ // loop second point - same y coord as destination;
// x is the col after the outermost dominated vertex
Point2D endVertexPoint = end.center;
@@ -733,27 +808,47 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
private void lighten(FGEdge e) {
+ if (!getLayoutOptions().useDimmedReturnEdges()) {
+ return;
+ }
+
// assumption: edges that move to the left in this layout are return flows that happen
// after the code block has been executed. We dim those a bit so that they
// produce less clutter.
e.setDefaultAlpha(.25);
}
- private Column getOutermostCol(LayoutLocationMap layoutLocations,
- Set vertices) {
+ private FGVertex getRightmostVertex(LayoutLocationMap layoutLocations,
+ Vertex2dFactory vertex2dFactory, Set vertices) {
- Column outermost = null;
+ List points = new ArrayList<>();
for (FGVertex v : vertices) {
- Column col = layoutLocations.col(v);
- if (outermost == null) {
- outermost = col;
+ Vertex2d v2d = vertex2dFactory.get(v);
+ points.add(v2d);
+ }
+
+ FGVertex v = getRightmostVertex(points);
+ return v;
+ }
+
+ private FGVertex getRightmostVertex(Collection points) {
+
+ Vertex2d rightmost = null;
+ for (Vertex2d v2d : points) {
+ if (rightmost == null) {
+ rightmost = v2d;
}
- else if (col.x > outermost.x) {
- outermost = col;
+ else {
+ // the rightmost is that which extends furthest to the right
+ double current = rightmost.getRight();
+ double other = v2d.getRight();
+ if (other > current) {
+ rightmost = v2d;
+ }
}
}
- return outermost;
+ return rightmost.v;
}
@Override
@@ -840,8 +935,11 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
BlockCopy copy = (BlockCopy) child;
StringBuilder buffy = new StringBuilder();
- buffy.append(printDepth(depth, depth + 1)).append(' ').append(ID).append(
- " plain - ").append(copy.getRef());
+ buffy.append(printDepth(depth, depth + 1))
+ .append(' ')
+ .append(ID)
+ .append(" plain - ")
+ .append(copy.getRef());
debug(buffy.toString());
}
@@ -958,7 +1056,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
//==================================================================================================
// Inner Classes
-//==================================================================================================
+//==================================================================================================
/**
* Encapsulates knowledge of edge direction (up/down, left/right) and uses that knowledge
@@ -1060,7 +1158,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
}
/**
- * A class that represents 2D information about the contained vertex, such as location,
+ * A class that represents 2D information about the contained vertex, such as location,
* bounds, row and column of the layout grid.
*/
private class Vertex2d {
@@ -1207,8 +1305,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
int row = startRow;
- for (int i = 0; i < allChildren.size(); i++) {
- DecompilerBlock block = allChildren.get(i);
+ for (DecompilerBlock block : allChildren) {
if (block instanceof DecompilerBlockGraph) {
row = ((DecompilerBlockGraph) block).setRows(row);
}
@@ -1229,8 +1326,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
String getChildrenString(int depth) {
StringBuilder buffy = new StringBuilder();
int childCount = 0;
- for (int i = 0; i < allChildren.size(); i++) {
- DecompilerBlock block = allChildren.get(i);
+ for (DecompilerBlock block : allChildren) {
if (block instanceof DecompilerBlockGraph) {
String blockName = block.getName();
@@ -1315,8 +1411,8 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
@Override
DecompilerBlock getBlock(FGVertex vertex) {
//
- // Note: we currently allow grouping in this layout. When we search for a vertex,
- // we have to check each vertex inside of the given group *and* each vertex
+ // Note: we currently allow grouping in this layout. When we search for a vertex,
+ // we have to check each vertex inside of the given group *and* each vertex
// inside of the vertex that belongs to this decompiler block.
//
if (vertex instanceof GroupedFunctionGraphVertex) {
@@ -1447,9 +1543,8 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// The 'list' structure for children's nesting:
// -all nodes are at the same level
//
- for (int i = 0; i < allChildren.size(); i++) {
+ for (DecompilerBlock block : allChildren) {
int column = col;
- DecompilerBlock block = allChildren.get(i);
block.setCol(column);
}
@@ -1477,8 +1572,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// -each successive condition is another level nested
//
int column = col;
- for (int i = 0; i < allChildren.size(); i++) {
- DecompilerBlock block = allChildren.get(i);
+ for (DecompilerBlock block : allChildren) {
block.setCol(column);
column++;
}
@@ -1515,11 +1609,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
//
// The 'do' structure for children's nesting:
- // -all blocks nested
+ // -all blocks nested
//
int column = col + 1;
- for (int i = 0; i < allChildren.size(); i++) {
- DecompilerBlock block = allChildren.get(i);
+ for (DecompilerBlock block : allChildren) {
block.setCol(column);
}
diff --git a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/ServerAdmin.java b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/ServerAdmin.java
index f5f3580822..5905018f95 100644
--- a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/ServerAdmin.java
+++ b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/ServerAdmin.java
@@ -52,7 +52,7 @@ public class ServerAdmin implements GhidraLaunchable {
* The following properties may be set:
*
* UserAdmin.invocation - identifies the name of the application used when displaying usage text.
- * UserAdmin.serverDir - identifies the server directory instead of passing on command line.
+ * UserAdmin.config - identifies the config file instead of passing on command line.
*
* @param args command line arguments
*/
@@ -75,22 +75,18 @@ public class ServerAdmin implements GhidraLaunchable {
* The following properties may be set:
*
* UserAdmin.invocation - identifies the name of the application used when displaying usage text.
- * UserAdmin.serverDir - identifies the server directory instead of passing on command line.
+ * UserAdmin.config - identifies the config file instead of passing on command line.
*
* @param args command line arguments
*/
public void execute(String[] args) {
- File serverDir = null;
-
int ix = 0;
- if (args.length != 0 && !args[0].startsWith("-")) {
- serverDir = new File(args[ix++]);
- }
- else {
- serverDir = getServerDirFromConfig();
- }
+ String configFilePath = args.length != 0 && !args[0].startsWith("-") ? args[ix++]
+ : System.getProperty(CONFIG_FILE_PROPERTY);
+
+ File serverDir = getServerDirFromConfig(configFilePath);
if (serverDir == null || (args.length - ix) == 0) {
displayUsage("");
System.exit(-1);
@@ -105,9 +101,7 @@ public class ServerAdmin implements GhidraLaunchable {
System.exit(-1);
}
- if (propertyUsed) {
- System.out.println("Using server directory: " + serverDir);
- }
+ System.out.println("Using server directory: " + serverDir);
File userFile = new File(serverDir, UserManager.USER_PASSWORD_FILE);
if (!serverDir.isDirectory() || !userFile.isFile()) {
@@ -423,13 +417,14 @@ public class ServerAdmin implements GhidraLaunchable {
}
}
- private File getServerDirFromConfig() {
- String p = System.getProperty(CONFIG_FILE_PROPERTY);
- if (p == null) {
+ private File getServerDirFromConfig(String configFilePath) {
+ System.out.println("Using config file: " + configFilePath);
+
+ if (configFilePath == null) {
return null;
}
- propertyUsed = true;
- File configFile = new File(p);
+
+ File configFile = new File(configFilePath);
if (!configFile.exists()) {
System.out.println("Config file not found: " + configFile.getAbsolutePath());
@@ -455,8 +450,9 @@ public class ServerAdmin implements GhidraLaunchable {
}
}
- p = config.getProperty(SERVER_DIR_CONFIG_PROPERTY);
+ String p = config.getProperty(SERVER_DIR_CONFIG_PROPERTY);
if (p == null) {
+ System.out.println("Failed to find property: " + SERVER_DIR_CONFIG_PROPERTY);
return null;
}
File dir = new File(p);
@@ -482,8 +478,8 @@ public class ServerAdmin implements GhidraLaunchable {
}
String invocationName = System.getProperty(INVOCATION_NAME_PROPERTY);
System.err.println("Usage: " +
- (invocationName != null ? invocationName : "java " + UserAdmin.class.getName()) +
- (propertyUsed ? "" : " ") + " [] [] ...");
+ (invocationName != null ? invocationName : "java " + ServerAdmin.class.getName()) +
+ (invocationName != null ? "" : " ") + " [] [] ...");
System.err.println("\nSupported commands:");
System.err.println(" -add [--p]");
System.err.println(
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java b/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java
index c0963c8883..d485574655 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/DockableComponent.java
@@ -151,6 +151,10 @@ public class DockableComponent extends JPanel implements ContainerListener {
}
private void showContextMenu(MouseEvent e) {
+ if (e.isConsumed()) {
+ return;
+ }
+
Component component = e.getComponent();
if (component == null) {
return; // not sure this can happen
@@ -190,7 +194,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
/**
* Returns the component provider attached to this dockable component; null if this object
* has been disposed
- *
+ *
* @return the provider
*/
public ComponentProvider getComponentProvider() {
@@ -255,7 +259,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
public synchronized void dragEnter(DropTargetDragEvent dtde) {
super.dragEnter(dtde);
- // On Mac, sometimes this component is not showing,
+ // On Mac, sometimes this component is not showing,
// which causes exception in the translate method.
if (!isShowing()) {
dtde.rejectDrag();
@@ -278,7 +282,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
public synchronized void dragOver(DropTargetDragEvent dtde) {
super.dragOver(dtde);
- // On Mac, sometimes this component is not showing,
+ // On Mac, sometimes this component is not showing,
// which causes exception in the translate method.
if (!isShowing()) {
dtde.rejectDrag();
@@ -457,7 +461,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
private void componentSelected(Component component) {
if (!component.isFocusable()) {
- // In this case, Java will not change focus for us, so we need to tell the DWM to
+ // In this case, Java will not change focus for us, so we need to tell the DWM to
// change the active DockableComponent
requestFocus();
}
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/framework/DockingApplicationLayout.java b/Ghidra/Framework/Docking/src/main/java/docking/framework/DockingApplicationLayout.java
index 7e0975539c..21758d2c8c 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/framework/DockingApplicationLayout.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/framework/DockingApplicationLayout.java
@@ -16,12 +16,12 @@
package docking.framework;
import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Objects;
+import java.util.*;
import generic.jar.ResourceFile;
import ghidra.framework.ApplicationProperties;
import ghidra.util.SystemUtilities;
+import util.CollectionUtils;
import utility.application.ApplicationLayout;
import utility.application.ApplicationUtilities;
import utility.module.ModuleUtilities;
@@ -34,16 +34,6 @@ public class DockingApplicationLayout extends ApplicationLayout {
private static final String NO_RELEASE_NAME = "NO_RELEASE";
- /**
- * Constructs a new docking application layout object with the given name.
- *
- * @param name The name of the application.
- * @throws FileNotFoundException if there was a problem getting a user directory.
- */
- public DockingApplicationLayout(String name) throws FileNotFoundException {
- this(name, "0.1");
- }
-
/**
* Constructs a new docking application layout object with the given name and version.
*
@@ -57,24 +47,31 @@ public class DockingApplicationLayout extends ApplicationLayout {
/**
* Constructs a new docking application layout object with the given set of application
- * properties.
+ * properties. The default Ghidra application root directory(s) will be used.
*
* @param applicationProperties The properties object that will be read system properties.
* @throws FileNotFoundException if there was a problem getting a user directory.
*/
public DockingApplicationLayout(ApplicationProperties applicationProperties)
throws FileNotFoundException {
+ this(getDefaultApplicationRootDirs(), applicationProperties);
+ }
+
+ /**
+ * Constructs a new docking application layout object with the given set of application
+ * properties.
+ *
+ * @param applicationRootDirs list of application root directories which should be
+ * used to idenitfy modules and resources. The first entry will be treated as the
+ * installation root.
+ * @param applicationProperties The properties object that will be read system properties.
+ * @throws FileNotFoundException if there was a problem getting a user directory.
+ */
+ public DockingApplicationLayout(Collection applicationRootDirs,
+ ApplicationProperties applicationProperties) throws FileNotFoundException {
this.applicationProperties = Objects.requireNonNull(applicationProperties);
-
- // Application root directories
- if (SystemUtilities.isInDevelopmentMode()) {
- applicationRootDirs = ApplicationUtilities.findDefaultApplicationRootDirs();
- }
- else {
- applicationRootDirs = new ArrayList<>();
- applicationRootDirs.add(new ResourceFile(System.getProperty("user.dir")));
- }
+ this.applicationRootDirs = applicationRootDirs;
// Application installation directory
applicationInstallationDir = applicationRootDirs.iterator().next().getParentFile();
@@ -97,4 +94,18 @@ public class DockingApplicationLayout extends ApplicationLayout {
applicationInstallationDir);
}
+ /**
+ * Get the default list of Application directories. In repo-based
+ * development mode this includes the root Ghidra directory within each repo.
+ * When not in development mode, the requirement is that the current working
+ * directory correspond to the installation root. The first entry will be
+ * the primary root in both cases.
+ * @return root directories
+ */
+ public static Collection getDefaultApplicationRootDirs() {
+ if (SystemUtilities.isInDevelopmentMode()) {
+ return ApplicationUtilities.findDefaultApplicationRootDirs();
+ }
+ return CollectionUtils.asList(new ResourceFile(System.getProperty("user.dir")));
+ }
}
diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/layout/AbstractVisualGraphLayout.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/layout/AbstractVisualGraphLayout.java
index 0fba3848f2..afecdf2477 100644
--- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/layout/AbstractVisualGraphLayout.java
+++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/layout/AbstractVisualGraphLayout.java
@@ -44,36 +44,36 @@ import ghidra.util.task.TaskMonitor;
* A base layout that marries the Visual Graph and Jung layout interfaces. This class allows
* you to create new layouts while stubbing the Jung layout methods.
*
- * This class essentially takes in client-produced grid row and column indices and
+ *
This class essentially takes in client-produced grid row and column indices and
* produces layout locations for those values.
*
- *
This an implementation the Jung {@link Layout} interface that handles most of the
+ *
This an implementation the Jung {@link Layout} interface that handles most of the
* layout implementation for you. Things to know:
*
* - You should call initialize() inside of your constructor
- * - You must implement {@link #performInitialGridLayout(VisualGraph)} - this is where
+ *
- You must implement {@link #performInitialGridLayout(VisualGraph)} - this is where
* you align your vertices (and optionally edge articulations) on a grid. This grid
* will be translated into layout space points for you.
- * - If you wish to use articulation points in your edges, you must override
- * {@link #usesEdgeArticulations()} to return true.
+ * - If you wish to use articulation points in your edges, you must override
+ * {@link #usesEdgeArticulations()} to return true.
*
- *
+ *
* By default, this class will create x-position values that
- * are aligned with the column's x-position. You can override
+ * are aligned with the column's x-position. You can override
* {@link #getVertexLocation(VisualVertex, Column, Row, Rectangle)} in order to center the
* vertex within its column
- * {@link #getCenteredVertexLocation(VisualVertex, Column, Row, Rectangle)}. Also note though
+ * {@link #getCenteredVertexLocation(VisualVertex, Column, Row, Rectangle)}. Also note though
* that if your layout returns true for {@link #isCondensedLayout()},
- * then the centering will be condensed and slightly off.
- *
+ * then the centering will be condensed and slightly off.
+ *
* @param the vertex type
* @param the edge type
- *
+ *
* @see GridLocationMap
* @see LayoutPositions
*/
//@formatter:off
-public abstract class AbstractVisualGraphLayout>
extends AbstractLayout
implements VisualGraphLayout {
@@ -106,9 +106,9 @@ public abstract class AbstractVisualGraphLayout newLayout) {
@@ -260,7 +260,7 @@ public abstract class AbstractVisualGraphLayout bends = edgesToBends.get(e);
if (bends == null) {
- // New edge is not in the old graph. This can happen if the old graph has
+ // New edge is not in the old graph. This can happen if the old graph has
// grouped vertices and some edges have been removed.
continue;
}
@@ -313,14 +313,7 @@ public abstract class AbstractVisualGraphLayout vertexLayoutLocations =
positionVerticesInLayoutSpace(transformer, vertices, layoutLocations);
- Map> edgeLayoutArticulationLocations =
- positionEdgeArticulationsInLayoutSpace(transformer, vertexLayoutLocations, edges,
- layoutLocations);
-
- // DEGUG triggers grid lines to be printed; useful for debugging
-// VisualGraphRenderer.DEBUG_ROW_COL_MAP.put((Graph, ?>) visualGraph,
-// layoutLocations.copy());
-
+ Map> edgeLayoutArticulationLocations = new HashMap<>();
Rectangle graphBounds =
getTotalGraphSize(vertexLayoutLocations, edgeLayoutArticulationLocations, transformer);
double centerX = graphBounds.getCenterX();
@@ -332,6 +325,12 @@ public abstract class AbstractVisualGraphLayout> 1);
int y = row.y + (row.getPaddedHeight(isCondensed) >> 1);
@@ -464,8 +463,8 @@ public abstract class AbstractVisualGraphLayout transformer, double centerX, double centerY) {
//
- // Note: we move the articulations and vertices closer together on the x-axis. We do
- // not move the y-axis, as that is already as close together as we would like at
+ // Note: we move the articulations and vertices closer together on the x-axis. We do
+ // not move the y-axis, as that is already as close together as we would like at
// this point.
//
double condenseFactor = getCondenseFactor();
@@ -496,14 +495,14 @@ public abstract class AbstractVisualGraphLayout> 1; // half width
int myHeight = vertexBounds.height >> 1; // half height
double x = vertexPoint.getX();
@@ -635,20 +634,20 @@ public abstract class AbstractVisualGraphLayout the vertex type
- * @param the edge type
+ * @param the edge type
*/
public class VisualGraphRenderer>
extends edu.uci.ics.jung.visualization.renderers.BasicRenderer {
@@ -44,7 +44,8 @@ public class VisualGraphRenderer
/**
* Used for displaying grid information for graph layouts
*/
- public static Map, LayoutLocationMap, ?>> DEBUG_ROW_COL_MAP = new HashMap<>();
+ public static Map, LayoutLocationMap, ?>> DEBUG_ROW_COL_MAP =
+ new HashMap<>();
private Renderer.EdgeLabel edgeLabelRenderer = new BasicEdgeLabelRenderer<>();
@@ -122,11 +123,15 @@ public class VisualGraphRenderer
edgeLabelRenderer.labelEdge(rc, layout, e, xform.apply(e));
}
+ @SuppressWarnings({ "unchecked", "rawtypes" }) // the types in the cast matter not
private void paintLayoutGridCells(RenderContext renderContext, Layout layout) {
// to enable this debug, search java files for commented-out uses of 'DEBUG_ROW_COL_MAP'
- Graph graph = layout.getGraph();
- LayoutLocationMap, ?> locationMap = DEBUG_ROW_COL_MAP.get(graph);
+ Layout key = layout;
+ if (layout instanceof ObservableCachingLayout) {
+ key = ((ObservableCachingLayout) layout).getDelegate();
+ }
+ LayoutLocationMap, ?> locationMap = DEBUG_ROW_COL_MAP.get(key);
if (locationMap == null) {
return;
}
diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/StandAloneApplication.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/StandAloneApplication.java
index 1d5e24c673..a66fb1f0d0 100644
--- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/StandAloneApplication.java
+++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/StandAloneApplication.java
@@ -33,7 +33,6 @@ import ghidra.framework.model.ToolServices;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.classfinder.ClassSearcher;
-import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.xml.GenericXMLOutputter;
import ghidra.util.xml.XmlUtilities;
@@ -52,7 +51,6 @@ public abstract class StandAloneApplication implements GenericStandAloneApplicat
/**
* Creates a new application using the given properties filename. The
* filename is expected reside in the current working directory.
- *
*
* The given properties file is expected to have the
* {@link ApplicationProperties#APPLICATION_NAME_PROPERTY} and
@@ -60,43 +58,59 @@ public abstract class StandAloneApplication implements GenericStandAloneApplicat
* set.
*
* @param propertiesFilename the name of the properties file.
+ * @throws IOException error causing application initialization failure
*/
- public StandAloneApplication(String propertiesFilename) {
-
- try {
- ApplicationProperties properties = ApplicationProperties.fromFile(propertiesFilename);
- String name = properties.getProperty(ApplicationProperties.APPLICATION_NAME_PROPERTY);
- if (name == null) {
- Msg.error(this,
- "The application.name property is not set in " + propertiesFilename);
- }
-
- String version =
- properties.getProperty(ApplicationProperties.APPLICATION_VERSION_PROPERTY);
- if (version == null) {
- Msg.error(this,
- "The application.name property is not set in " + propertiesFilename);
- }
-
- ApplicationLayout applicationLayout = new DockingApplicationLayout(properties);
- init(applicationLayout);
- }
- catch (IOException e) {
- throw new AssertException(e);
- }
+ public StandAloneApplication(String propertiesFilename) throws IOException {
+ this(new DockingApplicationLayout(readApplicationProperties(propertiesFilename)));
}
- public StandAloneApplication(String name, String version) {
+ /**
+ * Creates a new application using the specified application name
+ * and version.
+ * @param name application name
+ * @param version application version
+ * @throws IOException error causing application initialization failure
+ */
+ public StandAloneApplication(String name, String version) throws IOException {
+ this(new DockingApplicationLayout(name, version));
+ }
- // Setup application layout
- try {
- ApplicationLayout applicationLayout = new DockingApplicationLayout(name, version);
- init(applicationLayout);
- }
- catch (IOException e) {
- throw new AssertException(e);
+ /**
+ * reates a new application using the given application layout
+ * and associated application properties.
+ * @param applicationLayout application layout
+ */
+ public StandAloneApplication(ApplicationLayout applicationLayout) {
+ init(applicationLayout);
+ }
+
+ /**
+ * Read {@link ApplicationProperties} from the specified file path relative
+ * to the current working directory.
+ *
+ * The given properties file is expected to have the
+ * {@link ApplicationProperties#APPLICATION_NAME_PROPERTY} and
+ * {@link ApplicationProperties#APPLICATION_VERSION_PROPERTY} properties
+ * set.
+ * @param propertiesFilename the name of the properties file.
+ * @return application properties
+ * @throws IOException if file read error occurs
+ */
+ public static ApplicationProperties readApplicationProperties(String propertiesFilename)
+ throws IOException {
+ ApplicationProperties properties = ApplicationProperties.fromFile(propertiesFilename);
+ String name = properties.getProperty(ApplicationProperties.APPLICATION_NAME_PROPERTY);
+ if (name == null) {
+ Msg.error(StandAloneApplication.class,
+ "The application.name property is not set in " + propertiesFilename);
}
+ String version = properties.getProperty(ApplicationProperties.APPLICATION_VERSION_PROPERTY);
+ if (version == null) {
+ Msg.error(StandAloneApplication.class,
+ "The application.name property is not set in " + propertiesFilename);
+ }
+ return properties;
}
private void init(ApplicationLayout applicationLayout) {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/NamespaceUtils.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/NamespaceUtils.java
index a03b6323d4..dad12522fa 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/NamespaceUtils.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/NamespaceUtils.java
@@ -534,53 +534,7 @@ public class NamespaceUtils {
throws InvalidInputException {
Symbol namespaceSymbol = namespace.getSymbol();
- String name = namespaceSymbol.getName();
- SourceType originalSource = namespaceSymbol.getSource();
-
SymbolTable symbolTable = namespaceSymbol.getProgram().getSymbolTable();
-
- // Temporarily rename old namespace (it will be removed at the end)
- int count = 1;
- while (true) {
- String n = name + "_" + count++;
- try {
- namespaceSymbol.setName(n, SourceType.ANALYSIS);
- break;
- }
- catch (DuplicateNameException e) {
- // continue
- }
- catch (InvalidInputException e) {
- throw new AssertException(e);
- }
- }
-
- // create new class namespace
- GhidraClass classNamespace = null;
- try {
- classNamespace =
- symbolTable.createClass(namespace.getParentNamespace(), name, originalSource);
- }
- catch (DuplicateNameException e) {
- throw new AssertException(e);
- }
- catch (InvalidInputException e) {
- // The only cause of this exception can be assumed but we need to
- // avoid showing the user a temporary name
- throw new InvalidInputException(
- "Namespace contained within Function may not be converted to a class: " + name);
- }
-
- // move everything from old namespace into new class namespace
- try {
- for (Symbol s : symbolTable.getSymbols(namespace)) {
- s.setNamespace(classNamespace);
- }
- namespaceSymbol.delete();
- }
- catch (DuplicateNameException | InvalidInputException | CircularDependencyException e) {
- throw new AssertException(e);
- }
- return classNamespace;
+ return symbolTable.convertNamespaceToClass(namespace);
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
index 5ae5403822..d13259b763 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
@@ -1695,34 +1695,41 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
@Override
public void disassociate(DataType dataType) {
- UniversalID oldDtID = dataType.getUniversalID();
- SourceArchive sourceArchive = dataType.getSourceArchive();
- sourceArchive = resolveSourceArchive(sourceArchive);
- UniversalID id = sourceArchive == null ? DataTypeManager.LOCAL_ARCHIVE_UNIVERSAL_ID
- : sourceArchive.getSourceArchiveID();
- if (id.equals(getUniversalID())) {
- id = DataTypeManager.LOCAL_ARCHIVE_UNIVERSAL_ID;
- }
- if (id == DataTypeManager.LOCAL_ARCHIVE_UNIVERSAL_ID) {
- // Already local data type so no source archive associated.
- return;
- }
- // Set the source archive to null indicating no associated archive.
- dataType.setSourceArchive(null);
+ lock.acquire();
+ try {
+ UniversalID oldDtID = dataType.getUniversalID();
+ SourceArchive sourceArchive = dataType.getSourceArchive();
+ sourceArchive = resolveSourceArchive(sourceArchive);
+ UniversalID id = sourceArchive == null ? DataTypeManager.LOCAL_ARCHIVE_UNIVERSAL_ID
+ : sourceArchive.getSourceArchiveID();
+ if (id.equals(getUniversalID())) {
+ id = DataTypeManager.LOCAL_ARCHIVE_UNIVERSAL_ID;
+ }
+ if (id == DataTypeManager.LOCAL_ARCHIVE_UNIVERSAL_ID) {
+ // Already local data type so no source archive associated.
+ return;
+ }
- // Set the datatype's universal ID to a newly generated universal ID,
- // since we no longer want the source archive data type's universal ID.
- if (dataType instanceof DataTypeDB) {
- DataTypeDB dt = (DataTypeDB) dataType;
- dt.setUniversalID(UniversalIdGenerator.nextID());
+ // Set the source archive to null indicating no associated archive.
+ dataType.setSourceArchive(null);
+
+ // Set the datatype's universal ID to a newly generated universal ID,
+ // since we no longer want the source archive data type's universal ID.
+ if (dataType instanceof DataTypeDB) {
+ DataTypeDB dt = (DataTypeDB) dataType;
+ dt.setUniversalID(UniversalIdGenerator.nextID());
+ }
+
+ if (oldDtID != null) {
+ idsToDataTypeMap.removeDataType(sourceArchive, oldDtID);
+ }
+
+ dataTypeChanged(dataType);
}
-
- if (oldDtID != null) {
- idsToDataTypeMap.removeDataType(sourceArchive, oldDtID);
+ finally {
+ lock.release();
}
-
- dataTypeChanged(dataType);
}
private Collection filterOutNonSourceSettableDataTypes(
@@ -2694,9 +2701,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
try {
creatingDataType++;
- DBRecord record = functionDefAdapter.createRecord(name, funDef.getComment(), cat.getID(),
- DEFAULT_DATATYPE_ID, funDef.hasVarArgs(), funDef.getGenericCallingConvention(),
- sourceArchiveIdValue, universalIdValue, funDef.getLastChangeTime());
+ DBRecord record =
+ functionDefAdapter.createRecord(name, funDef.getComment(), cat.getID(),
+ DEFAULT_DATATYPE_ID, funDef.hasVarArgs(), funDef.getGenericCallingConvention(),
+ sourceArchiveIdValue, universalIdValue, funDef.getLastChangeTime());
FunctionDefinitionDB funDefDb =
new FunctionDefinitionDB(this, dtCache, functionDefAdapter, paramAdapter, record);
@@ -3696,7 +3704,13 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
@Override
public DataType getDataType(SourceArchive sourceArchive, UniversalID datatypeID) {
UniversalID sourceID = sourceArchive == null ? null : sourceArchive.getSourceArchiveID();
- return idsToDataTypeMap.getDataType(sourceID, datatypeID);
+ lock.acquire();
+ try {
+ return idsToDataTypeMap.getDataType(sourceID, datatypeID);
+ }
+ finally {
+ lock.release();
+ }
}
@Override
@@ -3829,7 +3843,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
monitor.setProgress(0);
monitor.setMaximum(orderedComposites.size());
monitor.setMessage("Updating Datatype Sizes...");
-
+
int count = 0;
for (CompositeDB c : orderedComposites) {
monitor.checkCanceled();
@@ -4162,7 +4176,11 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
Map idMap =
map.computeIfAbsent(sourceID, k -> new ConcurrentHashMap<>());
- final UniversalID sourceArchiveID = sourceID;
+ UniversalID sourceArchiveID = sourceID;
+
+ // note: this call is atomic and has a lock on the 'idMap'. It may call to a method
+ // that requires a db lock. As such, the call to computeIfAbsent() must be
+ // made while holding the db lock.
return idMap.computeIfAbsent(dataTypeID,
k -> findDataTypeForIDs(sourceArchiveID, dataTypeID));
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/GhidraClassDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/GhidraClassDB.java
index 7a93b36631..f5956b744d 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/GhidraClassDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/GhidraClassDB.java
@@ -1,6 +1,5 @@
/* ###
* 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.
@@ -19,8 +18,7 @@ package ghidra.program.database.symbol;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.CircularDependencyException;
import ghidra.program.model.listing.GhidraClass;
-import ghidra.program.model.symbol.Namespace;
-import ghidra.program.model.symbol.Symbol;
+import ghidra.program.model.symbol.*;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
@@ -63,6 +61,19 @@ class GhidraClassDB implements GhidraClass {
return symbol.getName();
}
+ public void setName(String name, SourceType source, boolean checkForDuplicates)
+ throws DuplicateNameException, InvalidInputException {
+
+ try {
+ symbol.doSetNameAndNamespace(name, symbol.getParentNamespace(), source,
+ checkForDuplicates);
+ }
+ catch (CircularDependencyException e) {
+ // can't happen since we are not changing the namespace
+ }
+
+ }
+
/**
* @see ghidra.program.model.symbol.Namespace#getID()
*/
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDB.java
index 3d4cedef45..cef6893c50 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDB.java
@@ -104,8 +104,9 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return false;
}
record = rec;
- address = symbolMgr.getAddressMap().decodeAddress(
- rec.getLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL));
+ address = symbolMgr.getAddressMap()
+ .decodeAddress(
+ rec.getLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL));
return true;
}
return false;
@@ -522,8 +523,8 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return source;
}
- @Override
- public void setNameAndNamespace(String newName, Namespace newNamespace, SourceType source)
+ public void doSetNameAndNamespace(String newName, Namespace newNamespace, SourceType source,
+ boolean checkForDuplicates)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
lock.acquire();
@@ -563,7 +564,11 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
if (!namespaceChange && !nameChange) {
return;
}
- symbolMgr.checkDuplicateSymbolName(address, newName, newNamespace, getSymbolType());
+
+ if (checkForDuplicates) {
+ symbolMgr.checkDuplicateSymbolName(address, newName, newNamespace,
+ getSymbolType());
+ }
}
if (record != null) {
@@ -617,6 +622,13 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
}
+ @Override
+ public void setNameAndNamespace(String newName, Namespace newNamespace, SourceType source)
+ throws DuplicateNameException, InvalidInputException, CircularDependencyException {
+
+ doSetNameAndNamespace(newName, newNamespace, source, true);
+ }
+
protected List getSymbolsDynamicallyRenamedByMyRename() {
return null;
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java
index 6dc5934ba9..f1fac019bf 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java
@@ -40,7 +40,6 @@ import ghidra.program.util.LanguageTranslator;
import ghidra.util.*;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
-import ghidra.util.task.TaskMonitorAdapter;
public class SymbolManager implements SymbolTable, ManagerDB {
@@ -128,7 +127,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* Find previously defined variable storage address
* @param storage variable storage
* @return previously defined variable storage address or null if not found
- * @throws IOException
+ * @throws IOException if there is database exception
*/
public Address findVariableStorageAddress(VariableStorage storage) throws IOException {
return variableStorageMgr.getVariableStorageAddress(storage, false);
@@ -183,7 +182,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* where all the moved external addresses will be placed.
* The triggering of this upgrade relies on the addition of the VariableManager which
* trigger an upgrade.
- * @param monitor
+ * @param monitor the task monitor
*/
private boolean upgradeOldNamespaceAddresses(TaskMonitor monitor)
throws IOException, CancelledException {
@@ -239,9 +238,9 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/**
* Upgrade old stack and register variable symbol address to variable addresses.
* Also force associated references to be updated to new variable addresses.
- * @param monitor
- * @throws IOException
- * @throws CancelledException
+ * @param monitor the task monitor
+ * @throws IOException if there is database exception
+ * @throws CancelledException if the operation is cancelled
*/
private void processOldVariableAddresses(TaskMonitor monitor)
throws IOException, CancelledException {
@@ -296,8 +295,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* No more sharing the same variable address for multiple variable symbols.
* Must split these up. Only reference to variable addresses should be the
* symbol address - reference refer to physical/stack addresses, and symbolIDs.
- * @param monitor
- * @throws CancelledException
+ * @param monitor the task monitor
+ * @throws CancelledException if the operation is cancelled
*/
public void migrateFromOldVariableStorageManager(TaskMonitor monitor)
throws CancelledException {
@@ -396,9 +395,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/**
* Add old local symbols
- * @param table
- * @throws IOException
- * @throws CancelledException
+ * @throws IOException if there is database exception
+ * @throws CancelledException if the operation is cancelled
*/
private void processOldLocalSymbols(TaskMonitor monitor)
throws IOException, CancelledException {
@@ -450,6 +448,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* @param oldAddr old address value from symbol table
* @param name symbol name
* @param isPrimary true if symbol is primary at oldAddr
+ * @throws IOException if there is database exception
*/
public static void saveLocalSymbol(DBHandle tmpHandle, long symbolID, long oldAddr, String name,
boolean isPrimary) throws IOException {
@@ -480,18 +479,15 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return;
}
- List symbolList = getSymbols(name, namespace);
- for (Symbol symbol : symbolList) {
- if (!symbol.getSymbolType().allowsDuplicates()) {
- throw new DuplicateNameException(
- "A " + symbol.getSymbolType() + " symbol with name " + name +
- " already exists in namespace " + symbol.getParentNamespace().getName());
- }
+ Symbol symbol = getFirstSymbol(name, namespace, s -> !s.getSymbolType().allowsDuplicates());
+ if (symbol != null) {
+ throw new DuplicateNameException(
+ "A " + symbol.getSymbolType() + " symbol with name " + name +
+ " already exists in namespace " + symbol.getParentNamespace().getName());
}
-
}
- /**
+ /*
* Convert the specified dynamic symbol to a named symbol. Both symbol removed and symbol added
* notifications are performed, although the symbol instance is changed and continues to be
* valid.
@@ -509,8 +505,9 @@ public class SymbolManager implements SymbolTable, ManagerDB {
Address address = symbol.getAddress();
symbolRemoved(symbol, address, symbol.getName(), oldKey, Namespace.GLOBAL_NAMESPACE_ID,
null);
- DBRecord record = adapter.createSymbol(newName, address, newParentID, SymbolType.LABEL, 0,
- 1, null, source);
+ DBRecord record =
+ adapter.createSymbol(newName, address, newParentID, SymbolType.LABEL, 0,
+ 1, null, source);
symbol.setRecord(record);// symbol object was morphed
symbolAdded(symbol);
}
@@ -987,10 +984,11 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return searchSymbolsByNamespaceFirst(name, namespace);
}
- // Try to find the symbols by searching through all the symbols with the given name and including
- // only those in the specified namespace. If there are too many symbols with the same name and
- // we are not in the global space, abandon this approach and instead search through all
- // the symbols in the namespace and only include those with the specified name.
+ // Try to find the symbols by searching through all the symbols with the given name
+ // and including only those in the specified namespace. If there are too many symbols
+ // with the same name and we are not in the global space, abandon this approach and
+ // instead search through all the symbols in the namespace and only include those with
+ // the specified name.
int count = 0;
List list = new ArrayList<>();
SymbolIterator symbols = getSymbols(name);
@@ -1009,6 +1007,42 @@ public class SymbolManager implements SymbolTable, ManagerDB {
}
}
+ // note: this could be public; adding it may be confusing due to the potential for having
+ // multiple symbols and not knowing when to call which method.
+ private Symbol getFirstSymbol(String name, Namespace namespace, Predicate test) {
+ if (namespace == null) {
+ namespace = namespaceMgr.getGlobalNamespace();
+ }
+
+ if (namespace.isExternal() &&
+ SymbolUtilities.isReservedExternalDefaultName(name, program.getAddressFactory())) {
+ return findFirstSymbol(name, namespace, test);
+ }
+
+ else if (namespace instanceof Function && hasDefaultVariablePrefix(name)) {
+ return findFirstSymbol(name, namespace, test);
+ }
+
+ // Try to find the symbols by searching through all the symbols with the given name
+ // and including only those in the specified namespace. If there are too many symbols
+ // with the same name and we are not in the global space, abandon this approach and
+ // instead search through all the symbols in the namespace and only include those with
+ // the specified name.
+ int count = 0;
+ SymbolIterator symbols = getSymbols(name);
+ for (Symbol s : symbols) {
+ if (++count == MAX_DUPLICATE_COUNT && !namespace.isGlobal()) {
+ return findFirstSymbol(name, namespace, test);
+ }
+ if (s.getParentNamespace().equals(namespace) &&
+ test.test(s)) {
+ return s;
+ }
+ }
+
+ return null;
+ }
+
/**
* Returns the list of symbols with the given name and namespace.
*
@@ -1143,7 +1177,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/**
* Returns the next available external symbol address
- * @return
+ * @return the address
*/
public Address getNextExternalSymbolAddress() {
int extID = 1;
@@ -1414,6 +1448,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
if (!symbol.isDynamic()) {
createLabelHistoryRecord(addr, oldName, newName, LabelHistory.RENAME);
}
+
program.symbolChanged(symbol, ChangeManager.DOCR_SYMBOL_RENAMED, addr, symbol, oldName,
newName);
}
@@ -1493,7 +1528,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
// fire event
program.symbolChanged(symbol, ChangeManager.DOCR_SYMBOL_REMOVED, addr, symbol, name,
- new Long(symbolID));
+ symbolID);
}
void externalEntryPointRemoved(Address addr) {
@@ -1543,11 +1578,6 @@ public class SymbolManager implements SymbolTable, ManagerDB {
Symbol sym;
- /**
- * Construct iterator which returns a single symbol
- *
- * @param addr
- */
SingleSymbolIterator(Symbol sym) {
this.sym = sym;
}
@@ -2175,8 +2205,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
void moveLabelHistory(Address oldAddress, Address address) {
try {
- historyAdapter.moveAddressRange(oldAddress, address, 1, addrMap,
- TaskMonitorAdapter.DUMMY_MONITOR);
+ historyAdapter.moveAddressRange(oldAddress, address, 1, addrMap, TaskMonitor.DUMMY);
}
catch (CancelledException e) {
// can't happen, used dummy monitor
@@ -2266,6 +2295,109 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return new NamespaceDB(s, namespaceMgr);
}
+ @Override
+ public GhidraClass convertNamespaceToClass(Namespace namespace) {
+
+ if (namespace instanceof GhidraClass) {
+ return (GhidraClass) namespace;
+ }
+
+ lock.acquire();
+ try {
+
+ checkIsValidNamespaceForMyProgram(namespace);
+
+ Symbol namespaceSymbol = namespace.getSymbol();
+ String name = namespaceSymbol.getName();
+ SourceType originalSource = namespaceSymbol.getSource();
+
+ // no duplicate check, since this class name will be set to that of the existing namespace
+ String tempName = name + System.nanoTime();
+ SymbolDB classSymbol =
+ doCreateSpecialSymbol(Address.NO_ADDRESS, tempName, namespace.getParentNamespace(),
+ SymbolType.CLASS, -1, -1, null, originalSource, false /*check for duplicate */);
+ GhidraClassDB classNamespace = new GhidraClassDB(classSymbol, namespaceMgr);
+
+ // move everything from old namespace into new class namespace
+ for (Symbol s : getSymbols(namespace)) {
+
+ // no duplicate check, since these symbols all lived under the existing namespace
+ ((SymbolDB) s).doSetNameAndNamespace(s.getName(), classNamespace, s.getSource(),
+ false /*check for duplicate */);
+ }
+
+ namespaceSymbol.delete();
+
+ // fix name now that the old namespace is deleted
+ classNamespace.setName(name, SourceType.ANALYSIS, false /*check for duplicate */);
+
+ return classNamespace;
+ }
+ catch (DuplicateNameException | InvalidInputException | CircularDependencyException e) {
+ throw new AssertException("Unexpected exception creating class from namespace", e);
+ }
+ finally {
+ lock.release();
+ }
+ }
+
+ private void checkIsValidNamespaceForMyProgram(Namespace namespace) {
+
+ if (namespace == null) {
+ return;
+ }
+
+ if (namespace == program.getGlobalNamespace()) {
+ return;
+ }
+
+ if (!(namespace instanceof NamespaceDB)) {
+ throw new IllegalArgumentException(
+ "Namespace is not a valid parent for symbols: " +
+ namespace.getClass());
+ }
+
+ SymbolDB dbSymbol = (SymbolDB) namespace.getSymbol();
+ if (program != dbSymbol.getProgram()) {
+ throw new IllegalArgumentException(
+ "Namespace symbol is from a different program");
+ }
+
+ // may throw a ConcurrentModificationException
+ dbSymbol.checkDeleted();
+ }
+
+ @Override
+ public Namespace getOrCreateNameSpace(Namespace parent, String name, SourceType source)
+ throws DuplicateNameException, InvalidInputException {
+
+ lock.acquire();
+ try {
+
+ checkIsValidNamespaceForMyProgram(parent);
+
+ Symbol namespaceSymbol = getFirstSymbol(name, parent, s -> {
+ return s.getSymbolType() == SymbolType.NAMESPACE ||
+ s.getSymbolType() == SymbolType.CLASS;
+ });
+
+ if (namespaceSymbol != null) {
+ return (Namespace) namespaceSymbol.getObject();
+ }
+
+ // Note: We know there are no namespaces with the name; do we still have to check for
+ // duplicates? Assuming yes, as another symbol type may exist with this name.
+ SymbolDB s =
+ doCreateSpecialSymbol(Address.NO_ADDRESS, name, parent, SymbolType.NAMESPACE, -1,
+ -1, null, source, true /*check for duplicates*/);
+ return new NamespaceDB(s, namespaceMgr);
+
+ }
+ finally {
+ lock.release();
+ }
+ }
+
@Override
public Symbol createSymbolPlaceholder(Address address, long id) {
return SymbolDB.createSymbolPlaceholder(this, address, id);
@@ -2293,12 +2425,24 @@ public class SymbolManager implements SymbolTable, ManagerDB {
SymbolType symbolType, long data1, int data2, String data3, SourceType source)
throws DuplicateNameException, InvalidInputException {
+ return doCreateSpecialSymbol(addr, name, parent, symbolType, data1, data2, data3, source,
+ true);
+ }
+
+ private SymbolDB doCreateSpecialSymbol(Address addr, String name, Namespace parent,
+ SymbolType symbolType, long data1, int data2, String data3, SourceType source,
+ boolean checkForDuplicates)
+ throws DuplicateNameException, InvalidInputException {
+
lock.acquire();
try {
parent = validateNamespace(parent, addr, symbolType);
source = validateSource(source, name, addr, symbolType);
name = validateName(name, source);
- checkDuplicateSymbolName(addr, name, parent, symbolType);
+
+ if (checkForDuplicates) {
+ checkDuplicateSymbolName(addr, name, parent, symbolType);
+ }
return doCreateSymbol(name, addr, parent, symbolType, data1, data2, data3, source);
}
@@ -2378,7 +2522,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
}
/**
- * Internal method for creating funcions symbols.
+ * Internal method for creating function symbols
+ *
* @param addr the address for the new symbol
* @param name the name of the new symbol
* @param namespace the namespace for the new symbol
@@ -2489,8 +2634,9 @@ public class SymbolManager implements SymbolTable, ManagerDB {
long data1, int data2, String data3, SourceType source) {
try {
- DBRecord record = adapter.createSymbol(name, addr, namespace.getID(), type, data1, data2,
- data3, source);
+ DBRecord record =
+ adapter.createSymbol(name, addr, namespace.getID(), type, data1, data2,
+ data3, source);
SymbolDB newSymbol = makeSymbol(addr, record, type);
symbolAdded(newSymbol);
@@ -2633,19 +2779,29 @@ public class SymbolManager implements SymbolTable, ManagerDB {
@Override
public Symbol getVariableSymbol(String name, Function function) {
- return findFirstSymbol(name, function, s -> {
+ return getFirstSymbol(name, function, s -> {
SymbolType t = s.getSymbolType();
return t == SymbolType.PARAMETER || t == SymbolType.LOCAL_VAR;
});
}
private Symbol getSpecificSymbol(String name, Namespace namespace, SymbolType type) {
- return findFirstSymbol(name, namespace, s -> s.getSymbolType() == type);
+ return getFirstSymbol(name, namespace, s -> s.getSymbolType() == type);
}
private Symbol findFirstSymbol(String name, Namespace namespace, Predicate test) {
- List symbols = getSymbols(name, namespace);
- return symbols.stream().filter(test).findFirst().orElse(null);
+ if (namespace == null) {
+ namespace = namespaceMgr.getGlobalNamespace();
+ }
+
+ SymbolIterator it = getSymbols(namespace);
+ while (it.hasNext()) {
+ Symbol s = it.next();
+ if (s.getName().equals(name) && test.test(s)) {
+ return s;
+ }
+ }
+ return null;
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolTable.java
index ad1253bdaf..08dda6d69e 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolTable.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolTable.java
@@ -15,8 +15,7 @@
*/
package ghidra.program.model.symbol;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
@@ -504,7 +503,7 @@ public interface SymbolTable {
* @param name name of the namespace
* @param source the source of this class namespace's symbol
* @return new class namespace
- * @throws DuplicateNameException thrown if another non function or lable symbol exists with the given name
+ * @throws DuplicateNameException thrown if another non function or label symbol exists with the given name
* @throws InvalidInputException throw if the name has invalid characters or is null
* @throws IllegalArgumentException if you try to set the source to 'Symbol.DEFAULT'.
*/
@@ -523,7 +522,7 @@ public interface SymbolTable {
* @param source the source of this external library's symbol
* @return the new Library namespace.
* @throws IllegalArgumentException if you try to set the source to 'Symbol.DEFAULT'.
- * @throws DuplicateNameException thrown if another non function or lable symbol exists with the given name
+ * @throws DuplicateNameException thrown if another non function or label symbol exists with the given name
*/
public Library createExternalLibrary(String name, SourceType source)
throws DuplicateNameException, InvalidInputException;
@@ -534,7 +533,7 @@ public interface SymbolTable {
* @param name the name of the new namespace
* @param source the source of this namespace's symbol
* @return the new Namespace object.
- * @throws DuplicateNameException thrown if another non function or lable symbol exists with the given name
+ * @throws DuplicateNameException thrown if another non function or label symbol exists with the given name
* @throws InvalidInputException if the name is invalid.
* @throws IllegalArgumentException if you try to set the source to 'Symbol.DEFAULT'.
*/
@@ -552,4 +551,32 @@ public interface SymbolTable {
*/
public Symbol createSymbolPlaceholder(Address address, long id);
+ /**
+ * Converts the given namespace to a class namespace
+ *
+ * @param namespace the namespace to convert
+ * @return the new class
+ * @throws IllegalArgumentException if the given parent namespace is from a different program
+ * than that of this symbol table
+ * @throws ConcurrentModificationException if the given parent namespace has been deleted
+ */
+ public GhidraClass convertNamespaceToClass(Namespace namespace);
+
+ /**
+ * Gets an existing namespace with the given name in the given parent. If no namespace exists,
+ * then one will be created.
+ *
+ * @param parent the parent namespace
+ * @param name the namespace name
+ * @param source the source type for the namespace if one is created
+ * @return the namespace
+ * @throws DuplicateNameException thrown if another non function or label symbol exists with
+ * the given name
+ * @throws InvalidInputException if the name is invalid
+ * @throws IllegalArgumentException if the given parent namespace is from a different program
+ * than that of this symbol table
+ * @throws ConcurrentModificationException if the given parent namespace has been deleted
+ */
+ public Namespace getOrCreateNameSpace(Namespace parent, String name, SourceType source)
+ throws DuplicateNameException, InvalidInputException;
}
diff --git a/Ghidra/Processors/SuperH4/data/languages/SuperH4.sinc b/Ghidra/Processors/SuperH4/data/languages/SuperH4.sinc
index cfb4adc8af..724c889b7f 100644
--- a/Ghidra/Processors/SuperH4/data/languages/SuperH4.sinc
+++ b/Ghidra/Processors/SuperH4/data/languages/SuperH4.sinc
@@ -518,15 +518,15 @@ N_0t_dbr1: @^N_0^"+,"^DBR is N_0 & DBR { export N_0; }
N_0t_bank1: @^N_0^"+" is N_0 { export N_0; }
-FR0_t: fr0 is OP_0 & fr0 { x:4 = 0; export x; } # dummy export, just looking for the display
+FR0_t: fr0 is OP_0 & fr0 { export fr0; }
XMTRX_t: "xmtrx" is OP_0 { x:4 = 0; export x; } # dummy export, just looking for the display
-mach_t: MACH is OP_0 & MACH { x:4 = 0; export x; } # dummy export, just looking for the display
+mach_t: MACH is OP_0 & MACH { export MACH; }
-macl_t: MACL is OP_0 & MACL { x:4 = 0; export x; } # dummy export, just looking for the display
+macl_t: MACL is OP_0 & MACL { export MACL; }
-fpul_t: FPUL is OP_0 & FPUL { x:4 = 0; export x; } # dummy export, just looking for the display
+fpul_t: FPUL is OP_0 & FPUL { export FPUL; }
fpscr_t: "FPSCR" is OP_0 { x:4 = 0; export x; } # dummy export, just looking for the display
diff --git a/Ghidra/RuntimeScripts/Linux/server/svrAdmin b/Ghidra/RuntimeScripts/Linux/server/svrAdmin
index 702ca83c49..deb47eec9d 100755
--- a/Ghidra/RuntimeScripts/Linux/server/svrAdmin
+++ b/Ghidra/RuntimeScripts/Linux/server/svrAdmin
@@ -45,8 +45,8 @@ fi
OWNER="$(grep '^wrapper.app.account=' "${CONFIG}" | sed -e 's/^.*=\(.*\)\s*.*$/\1/')"
if [ -z "${OWNER}" -o "${OWNER}" = "$(whoami)" ]; then
- VMARGS="-DUserAdmin.invocation=$(basename "${SCRIPT_FILE}") -DUserAdmin.config=\"${CONFIG}\""
- "${SCRIPT_DIR}"/../support/launch.sh fg svrAdmin "${MAXMEM}" "$VMARGS" ghidra.server.ServerAdmin "$@"
+ VMARGS="-DUserAdmin.invocation=$(basename "${SCRIPT_FILE}")"
+ "${SCRIPT_DIR}"/../support/launch.sh fg svrAdmin "${MAXMEM}" "$VMARGS" ghidra.server.ServerAdmin "${CONFIG}" "$@"
else
echo "Running svrAdmin with $SUDO as ${OWNER} ..."
$SUDO -u $OWNER "$0" "$@"
diff --git a/Ghidra/RuntimeScripts/Linux/support/buildGhidraJar b/Ghidra/RuntimeScripts/Linux/support/buildGhidraJar
index 66b87cb0c2..c378218c75 100755
--- a/Ghidra/RuntimeScripts/Linux/support/buildGhidraJar
+++ b/Ghidra/RuntimeScripts/Linux/support/buildGhidraJar
@@ -25,7 +25,7 @@ if [ ! -d "${GHIDRA_ROOT_DIR}" ]; then
fi
# Set required VMARGS for jar builder application
-APP_VMARGS="-DGhidraJarBuilder.Name=$(basename "${SCRIPT_FILE}") -DGhidra.Install.Root.Dir=\"${GHIDRA_ROOT_DIR}\" "
+APP_VMARGS="-DGhidraJarBuilder.Name=$(basename "${SCRIPT_FILE}")"
# Launch jar builder
"${SCRIPT_DIR}"/launch.sh "${LAUNCH_MODE}" Ghidra "${MAXMEM}" "${APP_VMARGS}" ghidra.util.GhidraJarBuilder -main ghidra.JarRun "$@"
diff --git a/Ghidra/RuntimeScripts/Windows/server/ghidraSvr.bat b/Ghidra/RuntimeScripts/Windows/server/ghidraSvr.bat
index 98a2bc2310..09a4979711 100644
--- a/Ghidra/RuntimeScripts/Windows/server/ghidraSvr.bat
+++ b/Ghidra/RuntimeScripts/Windows/server/ghidraSvr.bat
@@ -11,7 +11,7 @@ rem runtime which has been configured into the system PATH ahead of other Java
rem it may be necessary to explicitly specify the path to the installation by setting JAVA_HOME
rem below:
-rem set JAVA_HOME=
+rem set "JAVA_HOME="
setlocal enabledelayedexpansion
@@ -50,7 +50,7 @@ if "%IS_ADMIN%"=="NO" (
rem Find the script directory
rem %~dsp0 is location of current script under NT
-set _REALPATH=%~dp0
+set "_REALPATH=%~dp0"
set APP_NAME=ghidraSvr
set APP_LONG_NAME=Ghidra Server
@@ -64,26 +64,26 @@ if exist "%_REALPATH%..\Ghidra\" goto normal
rem NOTE: If adjusting JAVA command assignment - do not attempt to add parameters (e.g., -d64, -version:1.7, etc.)
rem Development Environment
-set GHIDRA_HOME=%_REALPATH%..\..\..\..
-set WRAPPER_CONF=%_REALPATH%..\..\Common\server\server.conf
-set DATA_DIR=%GHIDRA_HOME%\%MODULE_DIR%\build\data
-set CLASSPATH_FRAG=%GHIDRA_HOME%\%MODULE_DIR%\build\dev-meta\classpath.frag
-set LS_CPATH=%GHIDRA_HOME%\GhidraBuild\LaunchSupport\bin\main
+set "GHIDRA_HOME=%_REALPATH%..\..\..\.."
+set "WRAPPER_CONF=%_REALPATH%..\..\Common\server\server.conf"
+set "DATA_DIR=%GHIDRA_HOME%\%MODULE_DIR%\build\data"
+set "CLASSPATH_FRAG=%GHIDRA_HOME%\%MODULE_DIR%\build\dev-meta\classpath.frag"
+set "LS_CPATH=%GHIDRA_HOME%\GhidraBuild\LaunchSupport\bin\main"
goto lab1
:normal
-set GHIDRA_HOME=%_REALPATH%..
-set WRAPPER_CONF=%_REALPATH%server.conf
-set DATA_DIR=%GHIDRA_HOME%\%MODULE_DIR%\data
-set CLASSPATH_FRAG=%GHIDRA_HOME%\%MODULE_DIR%\data\classpath.frag
-set LS_CPATH=%GHIDRA_HOME%\support\LaunchSupport.jar
+set "GHIDRA_HOME=%_REALPATH%.."
+set "WRAPPER_CONF=%_REALPATH%server.conf"
+set "DATA_DIR=%GHIDRA_HOME%\%MODULE_DIR%\data"
+set "CLASSPATH_FRAG=%GHIDRA_HOME%\%MODULE_DIR%\data\classpath.frag"
+set "LS_CPATH=%GHIDRA_HOME%\support\LaunchSupport.jar"
:lab1
rem set WRAPPER_HOME to unpacked yajsw location (crazy FOR syntax to set variable from command output)
for /F "usebackq delims=" %%p in (`dir "%DATA_DIR%" /ad /b ^| findstr "^%WRAPPER_NAME_PREFIX%"`) do set WRAPPER_DIRNAME=%%p
-set WRAPPER_HOME=%DATA_DIR%\%WRAPPER_DIRNAME%
+set "WRAPPER_HOME=%DATA_DIR%\%WRAPPER_DIRNAME%"
if not exist "%WRAPPER_HOME%\" (
echo.
@@ -104,8 +104,7 @@ set ERROR=ERROR: JAVA_HOME is not set and no 'java' command could be found in yo
goto reportError
:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA=%JAVA_HOME%\bin\java.exe
+set "JAVA=%JAVA_HOME%\bin\java.exe"
if exist "%JAVA%" goto lab2
set ERROR=ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -122,15 +121,7 @@ if "%JAVA_HOME%" == "" (
)
rem reestablish JAVA path based upon final JAVA_HOME
-set JAVA=%JAVA_HOME%\bin\java.exe
-
-set OS_NAME=win32
-"%JAVA%" -version 2>&1 | findstr /I " 64-Bit " >NUL
-if errorlevel 0 (
- set OS_NAME=win64
-)
-
-set OS_DIR=%GHIDRA_HOME%\%MODULE_DIR%\os\%OS_NAME%
+set "JAVA=%JAVA_HOME%\bin\java.exe"
:: set DEBUG=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=*:18888
diff --git a/Ghidra/RuntimeScripts/Windows/server/svrAdmin.bat b/Ghidra/RuntimeScripts/Windows/server/svrAdmin.bat
index ea7993ca45..1f8de1281e 100644
--- a/Ghidra/RuntimeScripts/Windows/server/svrAdmin.bat
+++ b/Ghidra/RuntimeScripts/Windows/server/svrAdmin.bat
@@ -31,18 +31,18 @@ set MAXMEM=128M
:: '% ~' dereferences the value in param 0
:: 'd' - drive
:: 'p' - path (without filename)
-set SCRIPT_DIR=%~dp0
+set "SCRIPT_DIR=%~dp0"
:: Production Environment
-set CONFIG=%SCRIPT_DIR%.\server.conf
+set "CONFIG=%SCRIPT_DIR%.\server.conf"
if exist "%CONFIG%" goto continue
:: Development Environment
-set CONFIG=%SCRIPT_DIR%..\..\Common\server\server.conf
+set "CONFIG=%SCRIPT_DIR%..\..\Common\server\server.conf"
:continue
-set VMARGS=-DUserAdmin.invocation="%0" -DUserAdmin.config="%CONFIG%"
+set VMARGS=-DUserAdmin.invocation=%~n0
-call "%~dp0\..\support\launch.bat" fg svrAdmin "%MAXMEM%" "%VMARGS%" ghidra.server.ServerAdmin %*
+call "%~dp0\..\support\launch.bat" fg svrAdmin "%MAXMEM%" "%VMARGS%" ghidra.server.ServerAdmin "%CONFIG%" %*
diff --git a/Ghidra/RuntimeScripts/Windows/server/svrInstall.bat b/Ghidra/RuntimeScripts/Windows/server/svrInstall.bat
index 1314f8a174..2df9605318 100644
--- a/Ghidra/RuntimeScripts/Windows/server/svrInstall.bat
+++ b/Ghidra/RuntimeScripts/Windows/server/svrInstall.bat
@@ -3,7 +3,7 @@ setlocal
rem Find the script directory
rem %~dsp0 is location of current script under NT
-set _REALPATH=%~dp0
+set "_REALPATH=%~dp0"
call "%_REALPATH%\ghidraSvr" install
diff --git a/Ghidra/RuntimeScripts/Windows/server/svrUninstall.bat b/Ghidra/RuntimeScripts/Windows/server/svrUninstall.bat
index 75e1905064..ac5131418b 100644
--- a/Ghidra/RuntimeScripts/Windows/server/svrUninstall.bat
+++ b/Ghidra/RuntimeScripts/Windows/server/svrUninstall.bat
@@ -3,7 +3,7 @@ setlocal
rem Find the script directory
rem %~dsp0 is location of current script under NT
-set _REALPATH=%~dp0
+set "_REALPATH=%~dp0"
call "%_REALPATH%\ghidraSvr" uninstall
diff --git a/Ghidra/RuntimeScripts/Windows/support/analyzeHeadless.bat b/Ghidra/RuntimeScripts/Windows/support/analyzeHeadless.bat
index aa4545b18b..e972b07cae 100644
--- a/Ghidra/RuntimeScripts/Windows/support/analyzeHeadless.bat
+++ b/Ghidra/RuntimeScripts/Windows/support/analyzeHeadless.bat
@@ -24,7 +24,7 @@ set VMARG_LIST=-XX:ParallelGCThreads=2
set VMARG_LIST=%VMARG_LIST% -XX:CICompilerCount=2
:: store current path
-set filepath=%~dp0
+set "filepath=%~dp0"
:: Loop through parameters (if there aren't any, just continue) and store
:: in params variable.
diff --git a/Ghidra/RuntimeScripts/Windows/support/buildGhidraJar.bat b/Ghidra/RuntimeScripts/Windows/support/buildGhidraJar.bat
index 1e618fa944..07f1c4dddc 100644
--- a/Ghidra/RuntimeScripts/Windows/support/buildGhidraJar.bat
+++ b/Ghidra/RuntimeScripts/Windows/support/buildGhidraJar.bat
@@ -8,9 +8,9 @@ setlocal
set LAUNCH_MODE=fg
:: Sets SCRIPT_DIR to the directory that contains this file (ends with '\')
-set SCRIPT_DIR=%~dp0
+set "SCRIPT_DIR=%~dp0"
-set GHIDRA_ROOT_DIR=%SCRIPT_DIR%..\Ghidra
+set "GHIDRA_ROOT_DIR=%SCRIPT_DIR%..\Ghidra"
if exist "%GHIDRA_ROOT_DIR%" goto continue
echo This script does not support development mode use
@@ -18,6 +18,6 @@ exit /B 1
:continue
-set APP_VMARGS=-DGhidraJarBuilder.Name=%0 -DGhidra.Install.Root.Dir=%GHIDRA_ROOT_DIR%
+set APP_VMARGS=-DGhidraJarBuilder.Name=%~n0
call "%~dp0launch.bat" %LAUNCH_MODE% Ghidra "" "%APP_VMARGS%" ghidra.util.GhidraJarBuilder -main ghidra.JarRun %*
diff --git a/Ghidra/RuntimeScripts/Windows/support/createPdbXmlFiles.bat b/Ghidra/RuntimeScripts/Windows/support/createPdbXmlFiles.bat
index 44826dd39e..10a7c7d8f0 100644
--- a/Ghidra/RuntimeScripts/Windows/support/createPdbXmlFiles.bat
+++ b/Ghidra/RuntimeScripts/Windows/support/createPdbXmlFiles.bat
@@ -11,16 +11,16 @@
setlocal
REM Get parent of current folder
-set SCRIPT_DIR=%~dp0
+set "SCRIPT_DIR=%~dp0"
-set GHIDRA_DIR=%SCRIPT_DIR%..\Ghidra
+set "GHIDRA_DIR=%SCRIPT_DIR%..\Ghidra"
set OS_DIR=os
REM Production Environment
if exist "%GHIDRA_DIR%" goto continue
REM Development Environment
-set GHIDRA_DIR=%SCRIPT_DIR%..\..\..
+set "GHIDRA_DIR=%SCRIPT_DIR%..\..\.."
set OS_DIR=build\os
:continue
@@ -35,7 +35,7 @@ if exist "%PROGRAMFILES(X86)%" (
set OS_TYPE=win32
)
-set PDB_EXE=%GHIDRA_DIR%\Features\PDB\%OS_DIR%\%OS_TYPE%\pdb.exe
+set "PDB_EXE=%GHIDRA_DIR%\Features\PDB\%OS_DIR%\%OS_TYPE%\pdb.exe"
if not exist "%PDB_EXE%" (
echo "%PDB_EXE% not found"
diff --git a/Ghidra/RuntimeScripts/Windows/support/launch.bat b/Ghidra/RuntimeScripts/Windows/support/launch.bat
index 9bee1566cf..5621f4e895 100644
--- a/Ghidra/RuntimeScripts/Windows/support/launch.bat
+++ b/Ghidra/RuntimeScripts/Windows/support/launch.bat
@@ -36,7 +36,7 @@ for /f "tokens=2" %%# in ("%cmdcmdline%") do if /i "%%#" equ "/c" set DOUBLE_CLI
:: '% ~' dereferences the value in param 0
:: 'd' - drive
:: 'p' - path (without filename)
-set SUPPORT_DIR=%~dp0
+set "SUPPORT_DIR=%~dp0"
::
:: Parse arguments
@@ -63,20 +63,20 @@ goto showUsage
::
:: Production Environment
::
-set INSTALL_DIR=%SUPPORT_DIR%..\
-set CPATH=%INSTALL_DIR%Ghidra\Framework\Utility\lib\Utility.jar
-set LS_CPATH=%SUPPORT_DIR%LaunchSupport.jar
-set DEBUG_LOG4J=%SUPPORT_DIR%debug.log4j.xml
+set "INSTALL_DIR=%SUPPORT_DIR%..\"
+set "CPATH=%INSTALL_DIR%Ghidra\Framework\Utility\lib\Utility.jar"
+set "LS_CPATH=%SUPPORT_DIR%LaunchSupport.jar"
+set "DEBUG_LOG4J=%SUPPORT_DIR%debug.log4j.xml"
if exist "%INSTALL_DIR%Ghidra" goto continue2
::
:: Development Environment
::
-set INSTALL_DIR=%INSTALL_DIR%..\..\..\
-set CPATH=%INSTALL_DIR%Ghidra\Framework\Utility\bin\main
-set LS_CPATH=%INSTALL_DIR%GhidraBuild\LaunchSupport\bin\main
-set DEBUG_LOG4J=%INSTALL_DIR%Ghidra\RuntimeScripts\Common\support\debug.log4j.xml
+set "INSTALL_DIR=%INSTALL_DIR%..\..\..\"
+set "CPATH=%INSTALL_DIR%Ghidra\Framework\Utility\bin\main"
+set "LS_CPATH=%INSTALL_DIR%GhidraBuild\LaunchSupport\bin\main"
+set "DEBUG_LOG4J=%INSTALL_DIR%Ghidra\RuntimeScripts\Common\support\debug.log4j.xml"
:continue2
@@ -108,7 +108,7 @@ if "%JAVA_HOME%" == "" (
goto exit1
)
)
-set JAVA_CMD=%JAVA_HOME%\bin\java
+set "JAVA_CMD=%JAVA_HOME%\bin\java"
:: Get the configurable VM arguments from the launch properties
for /f "delims=*" %%i in ('java -cp "%LS_CPATH%" LaunchSupport "%INSTALL_DIR%\" -vmargs') do set VMARG_LIST=%VMARG_LIST% %%i
diff --git a/Ghidra/RuntimeScripts/Windows/support/pythonRun.bat b/Ghidra/RuntimeScripts/Windows/support/pythonRun.bat
index bc934c12a1..650e9f172b 100644
--- a/Ghidra/RuntimeScripts/Windows/support/pythonRun.bat
+++ b/Ghidra/RuntimeScripts/Windows/support/pythonRun.bat
@@ -26,6 +26,6 @@ set VMARG_LIST=-XX:ParallelGCThreads=2
set VMARG_LIST=%VMARG_LIST% -XX:CICompilerCount=2
:: store current path
-set filepath=%~dp0
+set "filepath=%~dp0"
call "%filepath%launch.bat" %LAUNCH_MODE% Ghidra-Python "%MAXMEM%" "%VMARG_LIST%" ghidra.python.PythonRun %params%