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 ] [-srczip ] [-bin ] [-main ]\n"); System.err.println(buf.toString()); @@ -992,17 +937,10 @@ public class GhidraJarBuilder implements GhidraLaunchable { usage(args); } - List ghidraDirs = new ArrayList<>(); File outputFile = null; File srczip = null; File extraBinDir = null; String mainClassArg = null; - boolean usingGradle = false; - - String property = System.getProperty(GHIDRA_DIR); - if (property != null) { - ghidraDirs.add(new File(property)); - } for (int i = 0; i < args.length; i++) { String arg = args[i]; @@ -1030,28 +968,14 @@ public class GhidraJarBuilder implements GhidraLaunchable { } mainClassArg = args[++i]; } - else if (arg.equals("-gradle")) { - if (i == args.length - 1) { - usage(args); - } - usingGradle = true; - parseLibsFile(args[++i]); - } - else if (arg.startsWith("-")) { + else { usage(args); } - else { - ghidraDirs.add(new File(arg)); - } - } - if (ghidraDirs.isEmpty()) { - usage(args); } if (outputFile == null) { outputFile = new File("ghidra.jar"); } - System.out.println("Ghidra dirs = " + ghidraDirs); System.out.println("Output file = " + outputFile); if (srczip != null) { System.out.println("Source Zip File = " + srczip); @@ -1061,13 +985,10 @@ public class GhidraJarBuilder implements GhidraLaunchable { } try { - GhidraJarBuilder builder = new GhidraJarBuilder(ghidraDirs); + GhidraJarBuilder builder = new GhidraJarBuilder(layout); if (mainClassArg != null) { builder.setMainClass(mainClassArg); } - if (usingGradle) { - builder.setGradleMode(); - } builder.addExcludedFileExtension(".pdf"); // builder.addExcludedFileExtension(".htm"); @@ -1082,10 +1003,10 @@ public class GhidraJarBuilder implements GhidraLaunchable { System.out.println("Exclude " + module.getName()); } - builder.buildJar(outputFile, extraBinDir, TaskMonitorAdapter.DUMMY_MONITOR); + builder.buildJar(outputFile, extraBinDir, TaskMonitor.DUMMY); if (srczip != null) { - builder.buildSrcZip(srczip, TaskMonitorAdapter.DUMMY_MONITOR); + builder.buildSrcZip(srczip, TaskMonitor.DUMMY); } } catch (Exception e) { @@ -1099,8 +1020,4 @@ public class GhidraJarBuilder implements GhidraLaunchable { return new File(ghidraRootDir, "application.properties"); } - private void setGradleMode() { - inGradleMode = true; - } - } diff --git a/Ghidra/Features/Base/src/main/java/org/apache/felix/framework/util/MultiReleaseContent.java b/Ghidra/Features/Base/src/main/java/org/apache/felix/framework/util/MultiReleaseContent.java index f7ec50de6a..0e6909f086 100644 --- a/Ghidra/Features/Base/src/main/java/org/apache/felix/framework/util/MultiReleaseContent.java +++ b/Ghidra/Features/Base/src/main/java/org/apache/felix/framework/util/MultiReleaseContent.java @@ -1,7 +1,8 @@ /* ### * IP: Apache License 2.0 - * - * This is fix for a bug found in the Felix Framework: + */ +/*** + * This is fix for a bug found in the Felix Framework: * * https://issues.apache.org/jira/browse/FELIX-6297 * @@ -9,8 +10,8 @@ * * THIS FILE SHOULD BE REMOVED WHEN THE ISSUE IS ADDRESSED. * - */ -/* + *** + * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -30,24 +31,15 @@ */ package org.apache.felix.framework.util; -import org.apache.felix.framework.cache.Content; -import org.osgi.framework.Version; - import java.io.IOException; import java.io.InputStream; import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.jar.Attributes; +import java.util.*; import java.util.jar.Manifest; +import org.apache.felix.framework.cache.Content; +import org.osgi.framework.Version; + public class MultiReleaseContent implements Content { private final Content m_content; diff --git a/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph.html b/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph.html index 9e5de3196f..b66dce8c8f 100644 --- a/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph.html +++ b/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph.html @@ -840,6 +840,22 @@ location when zooming from the middle-mouse. The default for this option is off, which triggers zoom to work from the center of the graph, regardless of the mouse location.

+

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%