GP-706 - Speed up namespace lookup and creation

This commit is contained in:
dragonmacher 2021-03-18 12:13:28 -04:00
parent 79fce9b032
commit ae69ba87d1
35 changed files with 862 additions and 633 deletions

View file

@ -17,10 +17,9 @@
//@category Examples //@category Examples
import java.io.File; import java.io.File;
import java.util.*; import java.util.List;
import generic.jar.ApplicationModule; import generic.jar.ApplicationModule;
import generic.jar.ResourceFile;
import ghidra.app.script.GhidraScript; import ghidra.app.script.GhidraScript;
import ghidra.framework.Application; import ghidra.framework.Application;
import ghidra.util.GhidraJarBuilder; import ghidra.util.GhidraJarBuilder;
@ -32,8 +31,7 @@ public class BuildGhidraJarScript extends GhidraScript {
@Override @Override
public void run() throws Exception { public void run() throws Exception {
GhidraJarBuilder builder = GhidraJarBuilder builder = new GhidraJarBuilder(Application.getApplicationLayout());
new GhidraJarBuilder(toFiles(Application.getApplicationRootDirectories()));
builder.setMainClass("ghidra.JarRun"); // default is ghidra.JarRun, only here if you want builder.setMainClass("ghidra.JarRun"); // default is ghidra.JarRun, only here if you want
// to change it to something else. // 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. // uncomment the following line to create a src zip for debugging.
// builder.buildSrcZip(new File(installDir, "GhidraSrc.zip"), monitor); // builder.buildSrcZip(new File(installDir, "GhidraSrc.zip"), monitor);
} }
private List<File> toFiles(Collection<ResourceFile> resourceFiles) {
List<File> fileList = new ArrayList<>();
for (ResourceFile resourceFile : resourceFiles) {
fileList.add(resourceFile.getFile(true));
}
return fileList;
}
} }

View file

@ -24,6 +24,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import org.apache.commons.lang3.StringUtils;
import docking.ActionContext; import docking.ActionContext;
import docking.ComponentProvider; import docking.ComponentProvider;
import docking.dnd.GenericDataFlavor; import docking.dnd.GenericDataFlavor;
@ -303,12 +305,9 @@ public class CodeBrowserClipboardProvider extends ByteCopier
private Transferable copyAddress() { private Transferable copyAddress() {
AddressSetView addressSet = getSelectedAddresses(); AddressSetView addressSet = getSelectedAddresses();
StringBuilder buffy = new StringBuilder();
AddressIterator it = addressSet.getAddresses(true); AddressIterator it = addressSet.getAddresses(true);
while (it.hasNext()) { String joined = StringUtils.join((Iterator<Address>) it, "\n");
buffy.append(it.next()).append('\n'); return createStringTransferable(joined);
}
return createStringTransferable(buffy.toString());
} }
protected Transferable copyCode(TaskMonitor monitor) { protected Transferable copyCode(TaskMonitor monitor) {
@ -377,8 +376,8 @@ public class CodeBrowserClipboardProvider extends ByteCopier
private boolean pasteLabelsComments(Transferable pasteData, boolean pasteLabels, private boolean pasteLabelsComments(Transferable pasteData, boolean pasteLabels,
boolean pasteComments) { boolean pasteComments) {
try { try {
List<?> list = (List<?>) pasteData.getTransferData( List<?> list =
CodeUnitInfoTransferable.localDataTypeFlavor); (List<?>) pasteData.getTransferData(CodeUnitInfoTransferable.localDataTypeFlavor);
List<CodeUnitInfo> infos = CollectionUtils.asList(list, CodeUnitInfo.class); List<CodeUnitInfo> infos = CollectionUtils.asList(list, CodeUnitInfo.class);
Command cmd = new CodeUnitInfoPasteCmd(currentLocation.getAddress(), infos, pasteLabels, Command cmd = new CodeUnitInfoPasteCmd(currentLocation.getAddress(), infos, pasteLabels,
pasteComments); pasteComments);
@ -420,7 +419,7 @@ public class CodeBrowserClipboardProvider extends ByteCopier
return pasteOperandField((OperandFieldLocation) currentLocation, labelName); 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); return maybePasteNonLabelString(labelName);
} }
@ -451,12 +450,12 @@ public class CodeBrowserClipboardProvider extends ByteCopier
String oldName = symbol.getName(); String oldName = symbol.getName();
Namespace namespace = symbol.getParentNamespace(); Namespace namespace = symbol.getParentNamespace();
Address symbolAddress = symbol.getAddress(); Address symbolAddress = symbol.getAddress();
RenameLabelCmd cmd = new RenameLabelCmd(symbolAddress, oldName, labelName, RenameLabelCmd cmd = new RenameLabelCmd(symbolAddress, oldName, labelName, namespace,
namespace, SourceType.USER_DEFINED); SourceType.USER_DEFINED);
return tool.execute(cmd, currentProgram); 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); return maybePasteNonLabelString(labelName);
} }
@ -646,7 +645,7 @@ public class CodeBrowserClipboardProvider extends ByteCopier
//================================================================================================== //==================================================================================================
// Unsupported Operations // Unsupported Operations
//================================================================================================== //==================================================================================================
@Override @Override
public void lostOwnership(Transferable transferable) { public void lostOwnership(Transferable transferable) {

View file

@ -441,6 +441,7 @@ public abstract class DemangledObject implements Demangled {
namespace = program.getGlobalNamespace(); namespace = program.getGlobalNamespace();
} }
SymbolTable symbolTable = program.getSymbolTable();
for (String namespaceName : getNamespaceList(typeNamespace)) { for (String namespaceName : getNamespaceList(typeNamespace)) {
// TODO - This is compensating for too long templates. We should probably genericize // 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? // same name is the same class--would that reflect reality?
namespaceName = ensureNameLength(namespaceName); namespaceName = ensureNameLength(namespaceName);
SymbolTable symbolTable = program.getSymbolTable(); try {
namespace =
List<Symbol> symbols = symbolTable.getSymbols(namespaceName, namespace); symbolTable.getOrCreateNameSpace(namespace, namespaceName, SourceType.IMPORTED);
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;
}
} }
else if (isPermittedNamespaceSymbol(namespaceSymbol, functionPermitted)) { catch (DuplicateNameException e) {
namespace = (Namespace) namespaceSymbol.getObject();
}
else {
Msg.error(DemangledObject.class, Msg.error(DemangledObject.class,
"Failed to create namespace due to name conflict: " + "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)); NamespaceUtils.getNamespaceQualifiedName(namespace, namespaceName, false));
break; break;
} }
} }
return namespace; return namespace;
} }
private static boolean isPermittedNamespaceSymbol(Symbol symbol, boolean functionPermitted) { private static boolean isPermittedNamespaceType(SymbolType symbolType,
SymbolType symbolType = symbol.getSymbolType(); boolean functionPermitted) {
if (symbolType == SymbolType.CLASS || symbolType == SymbolType.NAMESPACE) { if (symbolType == SymbolType.CLASS || symbolType == SymbolType.NAMESPACE) {
return true; return true;
} }

View file

@ -34,8 +34,7 @@ import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator; import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.*;
import ghidra.util.exception.NotFoundException;
public class VarnodeContext implements ProcessorContext { public class VarnodeContext implements ProcessorContext {
@ -88,7 +87,7 @@ public class VarnodeContext implements ProcessorContext {
this.program = program; this.program = program;
// make a copy, because we could be making new spaces. // 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); BAD_ADDRESS = addrFactory.getAddress(getAddressSpace("BAD_ADDRESS_SPACE"), 0);
@ -1435,8 +1434,24 @@ public class VarnodeContext implements ProcessorContext {
class OffsetAddressFactory extends DefaultAddressFactory { class OffsetAddressFactory extends DefaultAddressFactory {
OffsetAddressFactory(AddressFactory baseFactory) { OffsetAddressFactory(Program program) {
super(filterSpaces(baseFactory.getAllAddressSpaces())); // 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() { private int getNextUniqueID() {
@ -1466,17 +1481,4 @@ class OffsetAddressFactory extends DefaultAddressFactory {
return (type == AddressSpace.TYPE_SYMBOL); return (type == AddressSpace.TYPE_SYMBOL);
} }
private static AddressSpace[] filterSpaces(AddressSpace[] allSpaces) {
List<AddressSpace> 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]);
}
} }

View file

@ -25,28 +25,24 @@ import java.util.zip.*;
import generic.jar.*; import generic.jar.*;
import ghidra.GhidraApplicationLayout; import ghidra.GhidraApplicationLayout;
import ghidra.GhidraLaunchable; import ghidra.GhidraLaunchable;
import ghidra.framework.Application; import ghidra.framework.*;
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
import ghidra.framework.plugintool.dialog.ExtensionUtils; import ghidra.framework.plugintool.dialog.ExtensionUtils;
import ghidra.util.classfinder.ClassFinder; import ghidra.util.classfinder.ClassFinder;
import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
import utilities.util.FileUtilities; import utilities.util.FileUtilities;
import utility.application.ApplicationLayout;
import utility.module.ModuleUtilities; import utility.module.ModuleUtilities;
public class GhidraJarBuilder implements GhidraLaunchable { public class GhidraJarBuilder implements GhidraLaunchable {
private static final String ROOT = "_Root/"; private static final String ROOT = "_Root/";
private static final String ROOT_GHIDRA = "_Root/Ghidra/"; 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 // 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 final String INVOCATION_NAME_PROPERTY = "GhidraJarBuilder.Name";
private static HashMap<String, List<String>> libsMap = new HashMap<>();
private List<File> rootGhidraDirs = new ArrayList<>(); private List<File> rootGhidraDirs = new ArrayList<>();
private List<ApplicationModule> allModules; private List<ApplicationModule> allModules;
private Set<ApplicationModule> includedModules = new HashSet<>(); private Set<ApplicationModule> includedModules = new HashSet<>();
@ -57,19 +53,18 @@ public class GhidraJarBuilder implements GhidraLaunchable {
private Pattern extensionPointSuffixPattern; private Pattern extensionPointSuffixPattern;
private List<String> extensionPointClasses = new ArrayList<>(); private List<String> extensionPointClasses = new ArrayList<>();
private ClassLoader classLoader; private ClassLoader classLoader;
private boolean inGradleMode = false;
private Set<File> processedJars = new HashSet<>(); private Set<File> processedJars = new HashSet<>();
public GhidraJarBuilder() { public GhidraJarBuilder() {
// Required for GhidraLaunchable // Required for GhidraLaunchable
} }
public GhidraJarBuilder(List<File> rootDirs) throws IOException { public GhidraJarBuilder(ApplicationLayout layout) throws IOException {
for (File file : rootDirs) { for (ResourceFile file : layout.getApplicationRootDirs()) {
File rgd = file.getCanonicalFile(); File rgd = file.getFile(false).getCanonicalFile();
rootGhidraDirs.add(rgd); rootGhidraDirs.add(rgd);
} }
allModules = findAllModules(); allModules = findAllModules(layout);
Collections.sort(allModules); Collections.sort(allModules);
for (ApplicationModule module : allModules) { for (ApplicationModule module : allModules) {
if (includeByDefault(module)) { if (includeByDefault(module)) {
@ -203,7 +198,7 @@ public class GhidraJarBuilder implements GhidraLaunchable {
for (ApplicationModule module : moduleList) { for (ApplicationModule module : moduleList) {
writeModuleClassesAndResources(jar, module); writeModuleClassesAndResources(jar, module);
if (!excludeHelp && !inGradleMode) { if (!excludeHelp) {
writeModuleHelp(jar, module); writeModuleHelp(jar, module);
} }
} }
@ -360,16 +355,6 @@ public class GhidraJarBuilder implements GhidraLaunchable {
private void writeModuleClassesAndResources(Jar jar, ApplicationModule module) private void writeModuleClassesAndResources(Jar jar, ApplicationModule module)
throws CancelledException, IOException { 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 // NOTE: This only works in a distribution where the 3rd party jars live in each
// module's libs directory // module's libs directory
File binDir = new File(module.getModuleDir(), "bin/main"); File binDir = new File(module.getModuleDir(), "bin/main");
@ -380,18 +365,6 @@ public class GhidraJarBuilder implements GhidraLaunchable {
processLibDir(jar, module); processLibDir(jar, module);
} }
private void processExternalLibs(Jar jar, ApplicationModule module)
throws CancelledException, IOException {
List<String> 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) private void processLibDir(Jar jar, ApplicationModule module)
throws CancelledException, IOException { throws CancelledException, IOException {
File libDir = new File(module.getModuleDir(), "lib"); File libDir = new File(module.getModuleDir(), "lib");
@ -585,16 +558,34 @@ public class GhidraJarBuilder implements GhidraLaunchable {
} }
return manifest; return manifest;
} }
private List<ApplicationModule> findAllModules() { private List<ApplicationModule> findAllModules(ApplicationLayout layout) throws IOException {
List<ApplicationModule> modules = new ArrayList<>(); List<ApplicationModule> modules = new ArrayList<>();
for (File appRoot : rootGhidraDirs) {
findModules(appRoot, appRoot, modules); for (GModule module : layout.getModules().values()) {
findModules(appRoot, new File(appRoot, "../GPL"), modules); File moduleDir = module.getModuleRoot().getFile(false).getCanonicalFile();
File rootDir = getModuleRootDir(moduleDir);
modules.add(new ApplicationModule(rootDir, moduleDir));
} }
return modules; return modules;
} }
private File getModuleRootDir(File moduleDir) {
// Look in GPL directories too
List<File> 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) { private String getPathFromRoot(String rootPath, File file) {
String filePath = file.getAbsolutePath(); String filePath = file.getAbsolutePath();
@ -604,23 +595,6 @@ public class GhidraJarBuilder implements GhidraLaunchable {
return filePath.substring(rootPath.length() + 1); return filePath.substring(rootPath.length() + 1);
} }
private void findModules(File rootAppDir, File dir, List<ApplicationModule> 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) { private void checkExtensionPointClass(String path, InputStream inputStream) {
// remove .class // remove .class
path = path.substring(0, path.length() - 6); path = path.substring(0, path.length() - 6);
@ -931,44 +905,15 @@ public class GhidraJarBuilder implements GhidraLaunchable {
} }
} }
private static void parseLibsFile(String libsFilePath) {
try {
List<String> lines = FileUtilities.getLines(new File(libsFilePath));
List<String> 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) { private static void usage(String[] args) {
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
System.err.println("arg " + i + ": " + args[i]); System.err.println("arg " + i + ": " + args[i]);
} }
String invocationName = System.getProperty(INVOCATION_NAME_PROPERTY); String invocationName = System.getProperty(INVOCATION_NAME_PROPERTY);
String property = System.getProperty(GHIDRA_DIR);
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append("\nUsage: "); buf.append("\nUsage: ");
buf.append(invocationName != null ? invocationName : "GhidraJarBuilder "); buf.append(invocationName != null ? invocationName : "GhidraJarBuilder");
if (property == null) {
buf.append("<Installation Root Directory> [<Installation Root Directory> ...] ");
}
buf.append( buf.append(
" [-output <output file>] [-srczip <src zip output file>] [-bin <compiled classes dir>] [-main <main-class>]\n"); " [-output <output file>] [-srczip <src zip output file>] [-bin <compiled classes dir>] [-main <main-class>]\n");
System.err.println(buf.toString()); System.err.println(buf.toString());
@ -992,17 +937,10 @@ public class GhidraJarBuilder implements GhidraLaunchable {
usage(args); usage(args);
} }
List<File> ghidraDirs = new ArrayList<>();
File outputFile = null; File outputFile = null;
File srczip = null; File srczip = null;
File extraBinDir = null; File extraBinDir = null;
String mainClassArg = 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++) { for (int i = 0; i < args.length; i++) {
String arg = args[i]; String arg = args[i];
@ -1030,28 +968,14 @@ public class GhidraJarBuilder implements GhidraLaunchable {
} }
mainClassArg = args[++i]; mainClassArg = args[++i];
} }
else if (arg.equals("-gradle")) { else {
if (i == args.length - 1) {
usage(args);
}
usingGradle = true;
parseLibsFile(args[++i]);
}
else if (arg.startsWith("-")) {
usage(args); usage(args);
} }
else {
ghidraDirs.add(new File(arg));
}
}
if (ghidraDirs.isEmpty()) {
usage(args);
} }
if (outputFile == null) { if (outputFile == null) {
outputFile = new File("ghidra.jar"); outputFile = new File("ghidra.jar");
} }
System.out.println("Ghidra dirs = " + ghidraDirs);
System.out.println("Output file = " + outputFile); System.out.println("Output file = " + outputFile);
if (srczip != null) { if (srczip != null) {
System.out.println("Source Zip File = " + srczip); System.out.println("Source Zip File = " + srczip);
@ -1061,13 +985,10 @@ public class GhidraJarBuilder implements GhidraLaunchable {
} }
try { try {
GhidraJarBuilder builder = new GhidraJarBuilder(ghidraDirs); GhidraJarBuilder builder = new GhidraJarBuilder(layout);
if (mainClassArg != null) { if (mainClassArg != null) {
builder.setMainClass(mainClassArg); builder.setMainClass(mainClassArg);
} }
if (usingGradle) {
builder.setGradleMode();
}
builder.addExcludedFileExtension(".pdf"); builder.addExcludedFileExtension(".pdf");
// builder.addExcludedFileExtension(".htm"); // builder.addExcludedFileExtension(".htm");
@ -1082,10 +1003,10 @@ public class GhidraJarBuilder implements GhidraLaunchable {
System.out.println("Exclude " + module.getName()); System.out.println("Exclude " + module.getName());
} }
builder.buildJar(outputFile, extraBinDir, TaskMonitorAdapter.DUMMY_MONITOR); builder.buildJar(outputFile, extraBinDir, TaskMonitor.DUMMY);
if (srczip != null) { if (srczip != null) {
builder.buildSrcZip(srczip, TaskMonitorAdapter.DUMMY_MONITOR); builder.buildSrcZip(srczip, TaskMonitor.DUMMY);
} }
} }
catch (Exception e) { catch (Exception e) {
@ -1099,8 +1020,4 @@ public class GhidraJarBuilder implements GhidraLaunchable {
return new File(ghidraRootDir, "application.properties"); return new File(ghidraRootDir, "application.properties");
} }
private void setGradleMode() {
inGradleMode = true;
}
} }

View file

@ -1,7 +1,8 @@
/* ### /* ###
* IP: Apache License 2.0 * 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 * https://issues.apache.org/jira/browse/FELIX-6297
* *
@ -9,8 +10,8 @@
* *
* THIS FILE SHOULD BE REMOVED WHEN THE ISSUE IS ADDRESSED. * THIS FILE SHOULD BE REMOVED WHEN THE ISSUE IS ADDRESSED.
* *
*/ ***
/* *
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
@ -30,24 +31,15 @@
*/ */
package org.apache.felix.framework.util; 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.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.*;
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.jar.Manifest; import java.util.jar.Manifest;
import org.apache.felix.framework.cache.Content;
import org.osgi.framework.Version;
public class MultiReleaseContent implements Content public class MultiReleaseContent implements Content
{ {
private final Content m_content; private final Content m_content;

View file

@ -840,6 +840,22 @@
location when zooming from the middle-mouse. The default for this option is off, which 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.</P> triggers zoom to work from the center of the graph, regardless of the mouse location.</P>
<P>The <B>View Settings</B> option describes how the graph will be zoomed when it is first
loaded. The values are:</P>
<UL>
<LI><B>Start Fully Zoomed Out</B> - always start fully zoomed out so that the entire
graph can be seen.</LI>
<LI><B>Start Fully Zoomed In/B> - always start fully zoomed in on the vertex containing
the current location.</LI>
<LI><B>Remember User Settings</B> - keep the zoom level where the user previously left
it.</LI>
</UL>
<BR>
<BR>
<P>There are various edge color and highlight color options available to change. The <P>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.</P> highlight colors are those to be used when the flow animations take place.</P>
</BLOCKQUOTE> </BLOCKQUOTE>

View file

@ -49,6 +49,13 @@
notes on how edges are routed for this layout.) notes on how edges are routed for this layout.)
</P> </P>
</BLOCKQUOTE> </BLOCKQUOTE>
<BLOCKQUOTE>
<P>The <B>Use Dim Return Edges</B> 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.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE> </BLOCKQUOTE>

View file

@ -42,7 +42,7 @@ public class FunctionGraphOptions extends VisualGraphOptions {
private static final String EDGE_COLOR_CONDITIONAL_JUMP_KEY = "Edge Color - Conditional Jump "; private static final String EDGE_COLOR_CONDITIONAL_JUMP_KEY = "Edge Color - Conditional Jump ";
//@formatter:off //@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 = private static final String NAVIGATION_HISTORY_DESCRIPTION =
"Determines how the navigation history will be updated when using the Function Graph. " + "Determines how the navigation history will be updated when using the Function Graph. " +
"The basic options are:" + "The basic options are:" +
@ -185,8 +185,8 @@ public class FunctionGraphOptions extends VisualGraphOptions {
options.registerOption(SCROLL_WHEEL_PANS_KEY, getScrollWheelPans(), help, options.registerOption(SCROLL_WHEEL_PANS_KEY, getScrollWheelPans(), help,
SCROLL_WHEEL_PANS_DESCRIPTION); SCROLL_WHEEL_PANS_DESCRIPTION);
options.registerOption(GRAPH_BACKGROUND_COLOR_KEY, DEFAULT_GRAPH_BACKGROUND_COLOR, options.registerOption(GRAPH_BACKGROUND_COLOR_KEY, DEFAULT_GRAPH_BACKGROUND_COLOR, help,
help, GRAPH_BACKGROUND_COLOR_DESCRPTION); GRAPH_BACKGROUND_COLOR_DESCRPTION);
options.registerOption(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, DEFAULT_VERTEX_BACKGROUND_COLOR, options.registerOption(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, DEFAULT_VERTEX_BACKGROUND_COLOR,
help, DEFAULT_VERTEX_BACKGROUND_COLOR_DESCRPTION); help, DEFAULT_VERTEX_BACKGROUND_COLOR_DESCRPTION);

View file

@ -31,7 +31,12 @@ public class DNLayoutOptions implements FGLayoutOptions {
"edges should be routed around any intersecting vertex. When toggled off, edges will " + "edges should be routed around any intersecting vertex. When toggled off, edges will " +
"pass through any intersecting vertices."; "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 useEdgeRoutingAroundVertices;
private boolean useDimmedReturnEdges = true;
@Override @Override
public void registerOptions(Options options) { public void registerOptions(Options options) {
@ -40,21 +45,32 @@ public class DNLayoutOptions implements FGLayoutOptions {
options.registerOption(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices, options.registerOption(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices,
help, USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION); help, USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION);
options.registerOption(DIM_RETURN_EDGES_KEY, useDimmedReturnEdges, help,
DIM_RETURN_EDGES_DESCRIPTION);
} }
@Override @Override
public void loadOptions(Options options) { public void loadOptions(Options options) {
useEdgeRoutingAroundVertices = useEdgeRoutingAroundVertices =
options.getBoolean(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices); options.getBoolean(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices);
useDimmedReturnEdges = options.getBoolean(DIM_RETURN_EDGES_KEY, useDimmedReturnEdges);
} }
public boolean useEdgeRoutingAroundVertices() { public boolean useEdgeRoutingAroundVertices() {
return useEdgeRoutingAroundVertices; return useEdgeRoutingAroundVertices;
} }
public boolean useDimmedReturnEdges() {
return useDimmedReturnEdges;
}
@Override @Override
public boolean optionChangeRequiresRelayout(String optionName) { public boolean optionChangeRequiresRelayout(String optionName) {
// format: 'Nested Code Layout.Route Edges....' // 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);
} }
} }

View file

@ -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.FGVertex;
import ghidra.app.plugin.core.functiongraph.graph.vertex.GroupedFunctionGraphVertex; import ghidra.app.plugin.core.functiongraph.graph.vertex.GroupedFunctionGraphVertex;
import ghidra.graph.VisualGraph; import ghidra.graph.VisualGraph;
import ghidra.graph.viewer.GraphViewerUtils;
import ghidra.graph.viewer.layout.*; import ghidra.graph.viewer.layout.*;
import ghidra.graph.viewer.vertex.VisualGraphVertexShapeTransformer; import ghidra.graph.viewer.vertex.VisualGraphVertexShapeTransformer;
import ghidra.program.model.address.Address; 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. * A layout that uses the decompiler to show code nesting based upon conditional logic.
* *
* <p>Edges returning to the default code flow are painted lighter to de-emphasize them. This * <p>Edges returning to the default code flow are painted lighter to de-emphasize them. This
* could be made into an option. * could be made into an option.
* *
* <p>Edge routing herein defaults to 'simple routing'; 'complex routing' is a user option. * <p>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 * 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 * 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 * 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 * 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 * 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; complex routing is better when edges/relationships are more
* important to the user. * important to the user.
* *
* TODO ideas: * TODO ideas:
* -paint fallthrough differently for all, or just for those returning to the baseline * -paint fallthrough differently for all, or just for those returning to the baseline
*/ */
@ -113,7 +114,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
@Override @Override
protected double getCondenseFactor() { 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 // stand out
return .3; return .3;
} }
@ -201,7 +202,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
continue; continue;
} }
// //
// Special case: fallthrough--don't label this...not sure how to tell fallthrough. For // 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, // now assume that any column below or backwards is fallthrough. However,
// do label fallthrough if it is the only edge. // do label fallthrough if it is the only edge.
@ -231,10 +232,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
Map<FGEdge, List<Point2D>> newEdgeArticulations = new HashMap<>(); Map<FGEdge, List<Point2D>> 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. // 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 // 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. // updated for the condense factor.
int edgeOffset = isCondensedLayout() int edgeOffset = isCondensedLayout()
? (int) (VERTEX_TO_EDGE_ARTICULATION_PADDING * (1 - getCondenseFactor())) ? (int) (VERTEX_TO_EDGE_ARTICULATION_PADDING * (1 - getCondenseFactor()))
@ -242,7 +243,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
Vertex2dFactory vertex2dFactory = Vertex2dFactory vertex2dFactory =
new Vertex2dFactory(transformer, vertexLayoutLocations, layoutToGridMap, edgeOffset); new Vertex2dFactory(transformer, vertexLayoutLocations, layoutToGridMap, edgeOffset);
// //
// Route our edges! // Route our edges!
// //
for (FGEdge e : edges) { for (FGEdge e : edges) {
@ -262,11 +263,8 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
DecompilerBlock loop = block.getParentLoop(); DecompilerBlock loop = block.getParentLoop();
if (loop != null) { if (loop != null) {
Set<FGVertex> vertices = loop.getVertices(); List<Point2D> articulations =
routeUpwardLoop(layoutToGridMap, vertex2dFactory, start, end, loop);
Column outermostCol = getOutermostCol(layoutToGridMap, vertices);
Column loopEndColumn = layoutToGridMap.nextColumn(outermostCol);
List<Point2D> articulations = routeLoopEdge(start, end, loopEndColumn);
newEdgeArticulations.put(e, articulations); newEdgeArticulations.put(e, articulations);
continue; continue;
} }
@ -275,31 +273,31 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
List<Point2D> articulations = new ArrayList<>(); List<Point2D> articulations = new ArrayList<>();
// //
// Basic routing: // Basic routing:
// -leave the bottom of the start vertex // -leave the bottom of the start vertex
// -first bend at some constant offset // -first bend at some constant offset
// -move to right or left, to above the end vertex // -move to right or left, to above the end vertex
// -second bend above the end vertex at previous constant offset // -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. // overlapping, then they produce angles when only using two articulations.
// Thus, we create articulations that are behind the vertices to remove // Thus, we create articulations that are behind the vertices to remove
// the angles. This points will not be seen. // the angles. This points will not be seen.
// //
// //
// Complex routing: // Complex routing:
// -this mode will route edges around vertices // -this mode will route edges around vertices
// //
// One goal for complex edge routing is to prevent overlapping (simple edge routing // 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 // 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: // 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 right - leave from the right; arrive to the left
// -Edge to the left - leave from the left; arrive to the right // -Edge to the left - leave from the left; arrive to the right
// -Edge straight down - go straight down // -Edge straight down - go straight down
// //
// For each of the above offsets, there will be an amplifier based upon row/column // 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. // distances will have a larger offset/spacing.
// //
@ -331,14 +329,78 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
return newEdgeArticulations; return newEdgeArticulations;
} }
private List<Point2D> routeUpwardLoop(LayoutLocationMap<FGVertex, FGEdge> layoutToGridMap,
Vertex2dFactory vertex2dFactory, Vertex2d start, Vertex2d end, DecompilerBlock loop) {
Set<FGVertex> 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<Vertex2d> 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<Point2D> articulations = routeLoopEdge(start, end, x);
return articulations;
}
private List<Vertex2d> getVerticesInBounds(Vertex2dFactory vertex2dFactory, int startRow,
int endRow, int startColumn, int endColumn) {
if (startRow > endRow) { // going upwards
int temp = endRow;
endRow = startRow;
startRow = temp;
}
List<Vertex2d> 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, private void routeToTheRightGoingUpwards(Vertex2d start, Vertex2d end,
Vertex2dFactory vertex2dFactory, List<Point2D> articulations) { Vertex2dFactory vertex2dFactory, List<Point2D> 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 // 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 delta = start.rowIndex - end.rowIndex;
int multiplier = EDGE_ENDPOINT_DISTANCE_MULTIPLIER; int multiplier = EDGE_ENDPOINT_DISTANCE_MULTIPLIER;
@ -347,11 +409,16 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
} }
int distanceSpacing = delta * multiplier; 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. // 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 // 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. // updated for the condense factor.
int exaggerationFactor = 1; int exaggerationFactor = 1;
if (isCondensedLayout()) { if (isCondensedLayout()) {
exaggerationFactor = 2; // determined by trial-and-error; can be made into an option 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); y1 = Math.min(y1, startCenterY);
articulations.add(new Point2D.Double(x1, y1)); // point is hidden behind the vertex 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. // the x value, restrict the y to the range between the edge and the center.
double startRightX = start.getRight(); double startRightX = start.getRight();
double x2 = startRightX + VERTEX_BORDER_THICKNESS; // start at the end 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 // 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, // 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 // 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. // edges with close endpoints from intersecting edges with distant endpoints.
// //
int delta = end.rowIndex - start.rowIndex; int delta = end.rowIndex - start.rowIndex;
int multiplier = EDGE_ENDPOINT_DISTANCE_MULTIPLIER; 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 // 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 // space them further in towards the center. This will keep edges with close endpoints
// from intersecting edges with distant endpoints. // from intersecting edges with distant endpoints.
// //
int delta = end.rowIndex - start.rowIndex; int delta = end.rowIndex - start.rowIndex;
if (delta < 0) { if (delta < 0) {
@ -529,7 +596,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
double y1 = start.getY(); double y1 = start.getY();
articulations.add(new Point2D.Double(x1, y1)); // point is hidden behind the vertex 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. // the x value, restrict the y to the range between the edge and the center.
double x2 = x1; double x2 = x1;
double y2 = end.getTop() + VERTEX_BORDER_THICKNESS; double y2 = end.getTop() + VERTEX_BORDER_THICKNESS;
@ -556,19 +623,6 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
private void routeAroundColumnVertices(Vertex2d start, Vertex2d end, private void routeAroundColumnVertices(Vertex2d start, Vertex2d end,
Vertex2dFactory vertex2dFactory, List<Point2D> articulations, double edgeX) { Vertex2dFactory vertex2dFactory, List<Point2D> 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<Point2D> articulations, double edgeX) {
if (useSimpleRouting()) { if (useSimpleRouting()) {
return; return;
} }
@ -582,14 +636,34 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
startRow = end.rowIndex; 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<Vertex2d> toCheck = new LinkedList<>(); List<Vertex2d> toCheck = new LinkedList<>();
for (int row = startRow + 1; row < endRow; row++) { for (int row = startRow; row < endRow + 1; 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 for (int col = startColumn; col < endColumn + 1; col++) {
// large row delta values)
Vertex2d otherVertex = vertex2dFactory.get(row, column); // assume any other vertex in our column can clip (it will not clip when
if (otherVertex != null) { // the 'spacing' above pushes the edge away from this column, like for
toCheck.add(otherVertex); // 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 padding = VERTEX_TO_EDGE_AVOIDANCE_PADDING;
int distanceSpacing = padding + delta; // adding the delta makes overlap less likely 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. // 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 // 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. // updated for the condense factor.
int vertexToEdgeOffset = otherVertex.getEdgeOffset(); int vertexToEdgeOffset = otherVertex.getEdgeOffset();
int exaggerationFactor = 1; int exaggerationFactor = 1;
if (isCondensedLayout()) { 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 // no need to check the 'y' value, as the end vertex is above/below this one
if (vertexClipper.isClippingX(otherVertex, edgeX)) { if (vertexClipper.isClippingX(otherVertex, edgeX)) {
/* /*
Must route around this vertex - new points: Must route around this vertex - new points:
-p1 - just above the intersection point -p1 - just above the intersection point
-p2 - just past the left edge -p2 - just past the left edge
-p3 - just past the bottom of the vertex -p3 - just past the bottom of the vertex
-p4 - back at the original x value -p4 - back at the original x value
| |
.___| .___|
| .-----. | .-----.
| | | | | |
| '-----' | '-----'
'---. '---.
| |
*/ */
// p1 - same x; y just above vertex // p1 - same x; y just above vertex
@ -650,19 +729,19 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
double y = vertexClipper.getTopOffset(otherVertex, vertexToEdgeOffset); double y = vertexClipper.getTopOffset(otherVertex, vertexToEdgeOffset);
articulations.add(new Point2D.Double(x, y)); articulations.add(new Point2D.Double(x, y));
// Maybe merge points if they are too close together. Visually, many lines // Maybe merge points if they are too close together. Visually, many lines
// moving around intersecting vertices looks busy. When the intersecting // moving around intersecting vertices looks busy. When the intersecting
// vertices are close together, we remove some of the articulations in order to // vertices are close together, we remove some of the articulations in order to
// smooth out the edges. // smooth out the edges.
if (articulations.size() > 2) { if (articulations.size() > 2) {
/* /*
The last articulation is the one added before this method was called, which The last articulation is the one added before this method was called, which
lies just below the intersecting vertex. The articulation before that is 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 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 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 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); Point2D previousArticulation = articulations.get(articulations.size() - 2);
int closenessHeight = 50; int closenessHeight = 50;
@ -696,15 +775,11 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
return !getLayoutOptions().useEdgeRoutingAroundVertices(); return !getLayoutOptions().useEdgeRoutingAroundVertices();
} }
private List<Point2D> routeLoopEdge(Vertex2d start, Vertex2d end, Column loopEndColumn) { private List<Point2D> routeLoopEdge(Vertex2d start, Vertex2d end, double x) {
// going backwards // going backwards
List<Point2D> articulations = new ArrayList<>(); List<Point2D> 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 startRow = start.rowIndex;
int endRow = end.rowIndex; int endRow = end.rowIndex;
if (startRow > endRow) { // going upwards if (startRow > endRow) { // going upwards
@ -720,7 +795,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
Point2D first = new Point2D.Double(x, y1); Point2D first = new Point2D.Double(x, y1);
articulations.add(first); 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 // x is the col after the outermost dominated vertex
Point2D endVertexPoint = end.center; Point2D endVertexPoint = end.center;
@ -733,27 +808,47 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
private void lighten(FGEdge e) { private void lighten(FGEdge e) {
if (!getLayoutOptions().useDimmedReturnEdges()) {
return;
}
// assumption: edges that move to the left in this layout are return flows that happen // 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 // after the code block has been executed. We dim those a bit so that they
// produce less clutter. // produce less clutter.
e.setDefaultAlpha(.25); e.setDefaultAlpha(.25);
} }
private Column getOutermostCol(LayoutLocationMap<FGVertex, FGEdge> layoutLocations, private FGVertex getRightmostVertex(LayoutLocationMap<FGVertex, FGEdge> layoutLocations,
Set<FGVertex> vertices) { Vertex2dFactory vertex2dFactory, Set<FGVertex> vertices) {
Column outermost = null; List<Vertex2d> points = new ArrayList<>();
for (FGVertex v : vertices) { for (FGVertex v : vertices) {
Column col = layoutLocations.col(v); Vertex2d v2d = vertex2dFactory.get(v);
if (outermost == null) { points.add(v2d);
outermost = col; }
FGVertex v = getRightmostVertex(points);
return v;
}
private FGVertex getRightmostVertex(Collection<Vertex2d> points) {
Vertex2d rightmost = null;
for (Vertex2d v2d : points) {
if (rightmost == null) {
rightmost = v2d;
} }
else if (col.x > outermost.x) { else {
outermost = col; // 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 @Override
@ -840,8 +935,11 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
BlockCopy copy = (BlockCopy) child; BlockCopy copy = (BlockCopy) child;
StringBuilder buffy = new StringBuilder(); StringBuilder buffy = new StringBuilder();
buffy.append(printDepth(depth, depth + 1)).append(' ').append(ID).append( buffy.append(printDepth(depth, depth + 1))
" plain - ").append(copy.getRef()); .append(' ')
.append(ID)
.append(" plain - ")
.append(copy.getRef());
debug(buffy.toString()); debug(buffy.toString());
} }
@ -958,7 +1056,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================
/** /**
* Encapsulates knowledge of edge direction (up/down, left/right) and uses that knowledge * 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. * bounds, row and column of the layout grid.
*/ */
private class Vertex2d { private class Vertex2d {
@ -1207,8 +1305,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
int row = startRow; int row = startRow;
for (int i = 0; i < allChildren.size(); i++) { for (DecompilerBlock block : allChildren) {
DecompilerBlock block = allChildren.get(i);
if (block instanceof DecompilerBlockGraph) { if (block instanceof DecompilerBlockGraph) {
row = ((DecompilerBlockGraph) block).setRows(row); row = ((DecompilerBlockGraph) block).setRows(row);
} }
@ -1229,8 +1326,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
String getChildrenString(int depth) { String getChildrenString(int depth) {
StringBuilder buffy = new StringBuilder(); StringBuilder buffy = new StringBuilder();
int childCount = 0; int childCount = 0;
for (int i = 0; i < allChildren.size(); i++) { for (DecompilerBlock block : allChildren) {
DecompilerBlock block = allChildren.get(i);
if (block instanceof DecompilerBlockGraph) { if (block instanceof DecompilerBlockGraph) {
String blockName = block.getName(); String blockName = block.getName();
@ -1315,8 +1411,8 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
@Override @Override
DecompilerBlock getBlock(FGVertex vertex) { DecompilerBlock getBlock(FGVertex vertex) {
// //
// Note: we currently allow grouping in this layout. When we search for a 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 // we have to check each vertex inside of the given group *and* each vertex
// inside of the vertex that belongs to this decompiler block. // inside of the vertex that belongs to this decompiler block.
// //
if (vertex instanceof GroupedFunctionGraphVertex) { if (vertex instanceof GroupedFunctionGraphVertex) {
@ -1447,9 +1543,8 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// The 'list' structure for children's nesting: // The 'list' structure for children's nesting:
// -all nodes are at the same level // -all nodes are at the same level
// //
for (int i = 0; i < allChildren.size(); i++) { for (DecompilerBlock block : allChildren) {
int column = col; int column = col;
DecompilerBlock block = allChildren.get(i);
block.setCol(column); block.setCol(column);
} }
@ -1477,8 +1572,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// -each successive condition is another level nested // -each successive condition is another level nested
// //
int column = col; int column = col;
for (int i = 0; i < allChildren.size(); i++) { for (DecompilerBlock block : allChildren) {
DecompilerBlock block = allChildren.get(i);
block.setCol(column); block.setCol(column);
column++; column++;
} }
@ -1515,11 +1609,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// //
// The 'do' structure for children's nesting: // The 'do' structure for children's nesting:
// -all blocks nested // -all blocks nested
// //
int column = col + 1; int column = col + 1;
for (int i = 0; i < allChildren.size(); i++) { for (DecompilerBlock block : allChildren) {
DecompilerBlock block = allChildren.get(i);
block.setCol(column); block.setCol(column);
} }

View file

@ -52,7 +52,7 @@ public class ServerAdmin implements GhidraLaunchable {
* The following properties may be set: * The following properties may be set:
* <pre> * <pre>
* UserAdmin.invocation - identifies the name of the application used when displaying usage text. * 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.
* </pre> * </pre>
* @param args command line arguments * @param args command line arguments
*/ */
@ -75,22 +75,18 @@ public class ServerAdmin implements GhidraLaunchable {
* The following properties may be set: * The following properties may be set:
* <pre> * <pre>
* UserAdmin.invocation - identifies the name of the application used when displaying usage text. * 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.
* </pre> * </pre>
* @param args command line arguments * @param args command line arguments
*/ */
public void execute(String[] args) { public void execute(String[] args) {
File serverDir = null;
int ix = 0; 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) { if (serverDir == null || (args.length - ix) == 0) {
displayUsage(""); displayUsage("");
System.exit(-1); System.exit(-1);
@ -105,9 +101,7 @@ public class ServerAdmin implements GhidraLaunchable {
System.exit(-1); 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); File userFile = new File(serverDir, UserManager.USER_PASSWORD_FILE);
if (!serverDir.isDirectory() || !userFile.isFile()) { if (!serverDir.isDirectory() || !userFile.isFile()) {
@ -423,13 +417,14 @@ public class ServerAdmin implements GhidraLaunchable {
} }
} }
private File getServerDirFromConfig() { private File getServerDirFromConfig(String configFilePath) {
String p = System.getProperty(CONFIG_FILE_PROPERTY); System.out.println("Using config file: " + configFilePath);
if (p == null) {
if (configFilePath == null) {
return null; return null;
} }
propertyUsed = true;
File configFile = new File(p); File configFile = new File(configFilePath);
if (!configFile.exists()) { if (!configFile.exists()) {
System.out.println("Config file not found: " + configFile.getAbsolutePath()); 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) { if (p == null) {
System.out.println("Failed to find property: " + SERVER_DIR_CONFIG_PROPERTY);
return null; return null;
} }
File dir = new File(p); File dir = new File(p);
@ -482,8 +478,8 @@ public class ServerAdmin implements GhidraLaunchable {
} }
String invocationName = System.getProperty(INVOCATION_NAME_PROPERTY); String invocationName = System.getProperty(INVOCATION_NAME_PROPERTY);
System.err.println("Usage: " + System.err.println("Usage: " +
(invocationName != null ? invocationName : "java " + UserAdmin.class.getName()) + (invocationName != null ? invocationName : "java " + ServerAdmin.class.getName()) +
(propertyUsed ? "" : " <serverPath>") + " [<command>] [<command>] ..."); (invocationName != null ? "" : " <configPath>") + " [<command>] [<command>] ...");
System.err.println("\nSupported commands:"); System.err.println("\nSupported commands:");
System.err.println(" -add <sid> [--p]"); System.err.println(" -add <sid> [--p]");
System.err.println( System.err.println(

View file

@ -151,6 +151,10 @@ public class DockableComponent extends JPanel implements ContainerListener {
} }
private void showContextMenu(MouseEvent e) { private void showContextMenu(MouseEvent e) {
if (e.isConsumed()) {
return;
}
Component component = e.getComponent(); Component component = e.getComponent();
if (component == null) { if (component == null) {
return; // not sure this can happen 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 * Returns the component provider attached to this dockable component; null if this object
* has been disposed * has been disposed
* *
* @return the provider * @return the provider
*/ */
public ComponentProvider getComponentProvider() { public ComponentProvider getComponentProvider() {
@ -255,7 +259,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
public synchronized void dragEnter(DropTargetDragEvent dtde) { public synchronized void dragEnter(DropTargetDragEvent dtde) {
super.dragEnter(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. // which causes exception in the translate method.
if (!isShowing()) { if (!isShowing()) {
dtde.rejectDrag(); dtde.rejectDrag();
@ -278,7 +282,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
public synchronized void dragOver(DropTargetDragEvent dtde) { public synchronized void dragOver(DropTargetDragEvent dtde) {
super.dragOver(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. // which causes exception in the translate method.
if (!isShowing()) { if (!isShowing()) {
dtde.rejectDrag(); dtde.rejectDrag();
@ -457,7 +461,7 @@ public class DockableComponent extends JPanel implements ContainerListener {
private void componentSelected(Component component) { private void componentSelected(Component component) {
if (!component.isFocusable()) { 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 // change the active DockableComponent
requestFocus(); requestFocus();
} }

View file

@ -16,12 +16,12 @@
package docking.framework; package docking.framework;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.ArrayList; import java.util.*;
import java.util.Objects;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
import ghidra.framework.ApplicationProperties; import ghidra.framework.ApplicationProperties;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import util.CollectionUtils;
import utility.application.ApplicationLayout; import utility.application.ApplicationLayout;
import utility.application.ApplicationUtilities; import utility.application.ApplicationUtilities;
import utility.module.ModuleUtilities; import utility.module.ModuleUtilities;
@ -34,16 +34,6 @@ public class DockingApplicationLayout extends ApplicationLayout {
private static final String NO_RELEASE_NAME = "NO_RELEASE"; 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. * 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 * 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. * @param applicationProperties The properties object that will be read system properties.
* @throws FileNotFoundException if there was a problem getting a user directory. * @throws FileNotFoundException if there was a problem getting a user directory.
*/ */
public DockingApplicationLayout(ApplicationProperties applicationProperties) public DockingApplicationLayout(ApplicationProperties applicationProperties)
throws FileNotFoundException { 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<ResourceFile> applicationRootDirs,
ApplicationProperties applicationProperties) throws FileNotFoundException {
this.applicationProperties = Objects.requireNonNull(applicationProperties); this.applicationProperties = Objects.requireNonNull(applicationProperties);
this.applicationRootDirs = applicationRootDirs;
// Application root directories
if (SystemUtilities.isInDevelopmentMode()) {
applicationRootDirs = ApplicationUtilities.findDefaultApplicationRootDirs();
}
else {
applicationRootDirs = new ArrayList<>();
applicationRootDirs.add(new ResourceFile(System.getProperty("user.dir")));
}
// Application installation directory // Application installation directory
applicationInstallationDir = applicationRootDirs.iterator().next().getParentFile(); applicationInstallationDir = applicationRootDirs.iterator().next().getParentFile();
@ -97,4 +94,18 @@ public class DockingApplicationLayout extends ApplicationLayout {
applicationInstallationDir); 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<ResourceFile> getDefaultApplicationRootDirs() {
if (SystemUtilities.isInDevelopmentMode()) {
return ApplicationUtilities.findDefaultApplicationRootDirs();
}
return CollectionUtils.asList(new ResourceFile(System.getProperty("user.dir")));
}
} }

View file

@ -44,36 +44,36 @@ import ghidra.util.task.TaskMonitor;
* A base layout that marries the Visual Graph and Jung layout interfaces. This class allows * 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. * you to create new layouts while stubbing the Jung layout methods.
* *
* <P>This class essentially takes in client-produced grid row and column indices and * <P>This class essentially takes in client-produced grid row and column indices and
* produces layout locations for those values. * produces layout locations for those values.
* *
* <P>This an implementation the Jung {@link Layout} interface that handles most of the * <P>This an implementation the Jung {@link Layout} interface that handles most of the
* layout implementation for you. Things to know: * layout implementation for you. Things to know:
* <UL> * <UL>
* <LI>You should call initialize() inside of your constructor</LI> * <LI>You should call initialize() inside of your constructor</LI>
* <LI>You must implement {@link #performInitialGridLayout(VisualGraph)} - this is where * <LI>You must implement {@link #performInitialGridLayout(VisualGraph)} - this is where
* you align your vertices (and optionally edge articulations) on a grid. This grid * you align your vertices (and optionally edge articulations) on a grid. This grid
* will be translated into layout space points for you.</LI> * will be translated into layout space points for you.</LI>
* <LI>If you wish to use articulation points in your edges, you must override * <LI>If you wish to use articulation points in your edges, you must override
* {@link #usesEdgeArticulations()} to return true.</LI> * {@link #usesEdgeArticulations()} to return true.</LI>
* </UL> * </UL>
* *
* <p><a id="column_centering"></A>By default, this class will create x-position values that * <p><a id="column_centering"></A>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 * {@link #getVertexLocation(VisualVertex, Column, Row, Rectangle)} in order to center the
* vertex within its column * 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()}, * 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 <V> the vertex type * @param <V> the vertex type
* @param <E> the edge type * @param <E> the edge type
* *
* @see GridLocationMap * @see GridLocationMap
* @see LayoutPositions * @see LayoutPositions
*/ */
//@formatter:off //@formatter:off
public abstract class AbstractVisualGraphLayout<V extends VisualVertex, public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
E extends VisualEdge<V>> E extends VisualEdge<V>>
extends AbstractLayout<V, E> extends AbstractLayout<V, E>
implements VisualGraphLayout<V, E> { implements VisualGraphLayout<V, E> {
@ -106,9 +106,9 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
/** /**
* This is the method that is called to perform the actual layout. While this method is * This is the method that is called to perform the actual layout. While this method is
* running, the {@link #monitor} variable has been set so that you can call * running, the {@link #monitor} variable has been set so that you can call
* {@link TaskMonitor#checkCanceled()}. * {@link TaskMonitor#checkCanceled()}.
* *
* @param g the graph * @param g the graph
* @return the new grid location * @return the new grid location
* @throws CancelledException if the operation was cancelled * @throws CancelledException if the operation was cancelled
@ -151,10 +151,10 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
} }
/** /**
* Returns true if this layout is in a condensed mode, which means to reduce space * Returns true if this layout is in a condensed mode, which means to reduce space
* between vertices and edges. This is useful to save space. Subclasses may choose to * between vertices and edges. This is useful to save space. Subclasses may choose to
* have this setting controlled via an option that the user can toggle. * have this setting controlled via an option that the user can toggle.
* *
* @return true for a condensed layout * @return true for a condensed layout
*/ */
protected boolean isCondensedLayout() { protected boolean isCondensedLayout() {
@ -218,11 +218,11 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
} }
/** /**
* This class has implemented {@link #cloneLayout(VisualGraph)} in order to properly * This class has implemented {@link #cloneLayout(VisualGraph)} in order to properly
* initialize location information in the layout so that subclasses do not have to. Each * initialize location information in the layout so that subclasses do not have to. Each
* subclass still needs to create the new instance of the layout that is being cloned, as * subclass still needs to create the new instance of the layout that is being cloned, as
* this class does not know how to do so. * this class does not know how to do so.
* *
* @param newGraph the new graph for the new layout * @param newGraph the new graph for the new layout
* @return the new layout * @return the new layout
*/ */
@ -237,7 +237,7 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
/** /**
* Takes the given layout and copies the layout information this layout into that layout * Takes the given layout and copies the layout information this layout into that layout
* *
* @param newLayout the new layout to update * @param newLayout the new layout to update
*/ */
protected void initializeClonedLayout(AbstractVisualGraphLayout<V, E> newLayout) { protected void initializeClonedLayout(AbstractVisualGraphLayout<V, E> newLayout) {
@ -260,7 +260,7 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
List<Point2D> bends = edgesToBends.get(e); List<Point2D> bends = edgesToBends.get(e);
if (bends == null) { 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. // grouped vertices and some edges have been removed.
continue; continue;
} }
@ -313,14 +313,7 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
Map<V, Point2D> vertexLayoutLocations = Map<V, Point2D> vertexLayoutLocations =
positionVerticesInLayoutSpace(transformer, vertices, layoutLocations); positionVerticesInLayoutSpace(transformer, vertices, layoutLocations);
Map<E, List<Point2D>> edgeLayoutArticulationLocations = Map<E, List<Point2D>> edgeLayoutArticulationLocations = new HashMap<>();
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());
Rectangle graphBounds = Rectangle graphBounds =
getTotalGraphSize(vertexLayoutLocations, edgeLayoutArticulationLocations, transformer); getTotalGraphSize(vertexLayoutLocations, edgeLayoutArticulationLocations, transformer);
double centerX = graphBounds.getCenterX(); double centerX = graphBounds.getCenterX();
@ -332,6 +325,12 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
centerX, centerY); centerX, centerY);
} }
edgeLayoutArticulationLocations = positionEdgeArticulationsInLayoutSpace(transformer,
vertexLayoutLocations, edges, layoutLocations);
// DEGUG triggers grid lines to be printed; useful for debugging
// VisualGraphRenderer.DEBUG_ROW_COL_MAP.put(this, layoutLocations.copy());
layoutLocations.dispose(); layoutLocations.dispose();
gridLocations.dispose(); gridLocations.dispose();
@ -368,7 +367,7 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
/** /**
* Returns a location for the given vertex that is centered within its cell * Returns a location for the given vertex that is centered within its cell
* *
* @param v the vertex * @param v the vertex
* @param col the vertex's column in the grid * @param col the vertex's column in the grid
* @param row the vertex's row in the grid * @param row the vertex's row in the grid
@ -379,7 +378,7 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
// //
// Move x over to compensate for vertex painting. Edges are drawn from the center of the // Move x over to compensate for vertex painting. Edges are drawn from the center of the
// vertex. Thus, if you have vertices with two different widths, then the edge between // vertex. Thus, if you have vertices with two different widths, then the edge between
// them will not be straight *when the vertices are painted off-center on their column* // them will not be straight *when the vertices are painted off-center on their column*
// (which means they are left-aligned). By centering the vertex, the center points of // (which means they are left-aligned). By centering the vertex, the center points of
// the differently sized vertices (on the same column and different rows) will be aligned. // the differently sized vertices (on the same column and different rows) will be aligned.
// //
@ -423,7 +422,7 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
// //
// half-height offsets the articulation points, which keeps long edge lines from // half-height offsets the articulation points, which keeps long edge lines from
// overlapping as much // overlapping as much
// //
boolean isCondensed = isCondensedLayout(); boolean isCondensed = isCondensedLayout();
int x = col.x + (col.getPaddedWidth(isCondensed) >> 1); int x = col.x + (col.getPaddedWidth(isCondensed) >> 1);
int y = row.y + (row.getPaddedHeight(isCondensed) >> 1); int y = row.y + (row.getPaddedHeight(isCondensed) >> 1);
@ -464,8 +463,8 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
VisualGraphVertexShapeTransformer<V> transformer, double centerX, double centerY) { VisualGraphVertexShapeTransformer<V> transformer, double centerX, double centerY) {
// //
// Note: we move the articulations and vertices closer together on the x-axis. We do // 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 // not move the y-axis, as that is already as close together as we would like at
// this point. // this point.
// //
double condenseFactor = getCondenseFactor(); double condenseFactor = getCondenseFactor();
@ -496,14 +495,14 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
} }
// //
// The above aggressive condensing may lead to neighboring node overlapping for // The above aggressive condensing may lead to neighboring node overlapping for
// nodes in the same row. Check to see if we need to move the nodes to avoid this case. // nodes in the same row. Check to see if we need to move the nodes to avoid this case.
// //
unclip(rows, newLocations, transformer); unclip(rows, newLocations, transformer);
} }
/** /**
* The amount (from 0 to 1.0) by which to condense the vertices of the graph when that * The amount (from 0 to 1.0) by which to condense the vertices of the graph when that
* feature is enabled. The default is .5 (50%). A value of 1.0 would be fully-condensed * feature is enabled. The default is .5 (50%). A value of 1.0 would be fully-condensed
* such that all vertices are aligned on the x-axis on the center of the graph. * such that all vertices are aligned on the x-axis on the center of the graph.
* @return the condense factor * @return the condense factor
@ -601,7 +600,7 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
// //
// Visual points (after the centering has taken place). Update the location to account // Visual points (after the centering has taken place). Update the location to account
// for this centering before checking for clipping. // for this centering before checking for clipping.
// //
int myWidth = vertexBounds.width >> 1; // half width int myWidth = vertexBounds.width >> 1; // half width
int myHeight = vertexBounds.height >> 1; // half height int myHeight = vertexBounds.height >> 1; // half height
double x = vertexPoint.getX(); double x = vertexPoint.getX();
@ -635,20 +634,20 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
double newX = otherX + offset; double newX = otherX + offset;
vertexPoint.setLocation(newX, oldY); // editing this point changes the map's value vertexPoint.setLocation(newX, oldY); // editing this point changes the map's value
// DEBUG this can be deleted in the future, future, future // DEBUG this can be deleted in the future, future, future
// //@formatter:off // //@formatter:off
// Msg.debug(this, // Msg.debug(this,
// vertex + // vertex +
// "\n\tat " + vertexPoint.getX() + // "\n\tat " + vertexPoint.getX() +
// "\n\tvisual x: " + myNewPoint + // "\n\tvisual x: " + myNewPoint +
// "\n\tw: " + vertexBounds.width + // "\n\tw: " + vertexBounds.width +
// "\n\t\t" + otherVertex + // "\n\t\t" + otherVertex +
// "\n\t\tat: " + otherVertexPoint + // "\n\t\tat: " + otherVertexPoint +
// "\n\t\tvisual x: " + otherNewPoint.getX() + // "\n\t\tvisual x: " + otherNewPoint.getX() +
// "\n\t\tw: " + otherVertexBounds.width + // "\n\t\tw: " + otherVertexBounds.width +
// "\n\tclip: " + intersection.width + // "\n\tclip: " + intersection.width +
// "\n\toffset: " + offset + // "\n\toffset: " + offset +
// "\n\tnew pt: " + newX); // "\n\tnew pt: " + newX);
// //@formatter:on // //@formatter:on
} }

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -22,8 +22,8 @@ import java.util.*;
import com.google.common.base.Function; import com.google.common.base.Function;
import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.*; import edu.uci.ics.jung.visualization.*;
import edu.uci.ics.jung.visualization.layout.ObservableCachingLayout;
import edu.uci.ics.jung.visualization.renderers.Renderer; import edu.uci.ics.jung.visualization.renderers.Renderer;
import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator; import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator;
import ghidra.graph.viewer.*; import ghidra.graph.viewer.*;
@ -34,9 +34,9 @@ import ghidra.graph.viewer.layout.*;
* This was created to add the ability to paint selected vertices above other vertices. We need * This was created to add the ability to paint selected vertices above other vertices. We need
* this since the Jung Graph has no notion of Z-order and thus does not let us specify that any * this since the Jung Graph has no notion of Z-order and thus does not let us specify that any
* particular vertex should be above another one. * particular vertex should be above another one.
* *
* @param <V> the vertex type * @param <V> the vertex type
* @param <E> the edge type * @param <E> the edge type
*/ */
public class VisualGraphRenderer<V extends VisualVertex, E extends VisualEdge<V>> public class VisualGraphRenderer<V extends VisualVertex, E extends VisualEdge<V>>
extends edu.uci.ics.jung.visualization.renderers.BasicRenderer<V, E> { extends edu.uci.ics.jung.visualization.renderers.BasicRenderer<V, E> {
@ -44,7 +44,8 @@ public class VisualGraphRenderer<V extends VisualVertex, E extends VisualEdge<V>
/** /**
* Used for displaying grid information for graph layouts * Used for displaying grid information for graph layouts
*/ */
public static Map<Graph<?, ?>, LayoutLocationMap<?, ?>> DEBUG_ROW_COL_MAP = new HashMap<>(); public static Map<VisualGraphLayout<?, ?>, LayoutLocationMap<?, ?>> DEBUG_ROW_COL_MAP =
new HashMap<>();
private Renderer.EdgeLabel<V, E> edgeLabelRenderer = new BasicEdgeLabelRenderer<>(); private Renderer.EdgeLabel<V, E> edgeLabelRenderer = new BasicEdgeLabelRenderer<>();
@ -122,11 +123,15 @@ public class VisualGraphRenderer<V extends VisualVertex, E extends VisualEdge<V>
edgeLabelRenderer.labelEdge(rc, layout, e, xform.apply(e)); edgeLabelRenderer.labelEdge(rc, layout, e, xform.apply(e));
} }
@SuppressWarnings({ "unchecked", "rawtypes" }) // the types in the cast matter not
private void paintLayoutGridCells(RenderContext<V, E> renderContext, Layout<V, E> layout) { private void paintLayoutGridCells(RenderContext<V, E> renderContext, Layout<V, E> layout) {
// to enable this debug, search java files for commented-out uses of 'DEBUG_ROW_COL_MAP' // to enable this debug, search java files for commented-out uses of 'DEBUG_ROW_COL_MAP'
Graph<V, E> graph = layout.getGraph(); Layout<V, E> key = layout;
LayoutLocationMap<?, ?> locationMap = DEBUG_ROW_COL_MAP.get(graph); if (layout instanceof ObservableCachingLayout) {
key = ((ObservableCachingLayout) layout).getDelegate();
}
LayoutLocationMap<?, ?> locationMap = DEBUG_ROW_COL_MAP.get(key);
if (locationMap == null) { if (locationMap == null) {
return; return;
} }

View file

@ -33,7 +33,6 @@ import ghidra.framework.model.ToolServices;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.xml.GenericXMLOutputter; import ghidra.util.xml.GenericXMLOutputter;
import ghidra.util.xml.XmlUtilities; 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 * Creates a new application using the given properties filename. The
* filename is expected reside in the current working directory. * filename is expected reside in the current working directory.
*
* <p> * <p>
* <b>The given properties file is expected to have the * <b>The given properties file is expected to have the
* {@link ApplicationProperties#APPLICATION_NAME_PROPERTY} and * {@link ApplicationProperties#APPLICATION_NAME_PROPERTY} and
@ -60,43 +58,59 @@ public abstract class StandAloneApplication implements GenericStandAloneApplicat
* set.</b> * set.</b>
* *
* @param propertiesFilename the name of the properties file. * @param propertiesFilename the name of the properties file.
* @throws IOException error causing application initialization failure
*/ */
public StandAloneApplication(String propertiesFilename) { public StandAloneApplication(String propertiesFilename) throws IOException {
this(new DockingApplicationLayout(readApplicationProperties(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 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 { * reates a new application using the given application layout
ApplicationLayout applicationLayout = new DockingApplicationLayout(name, version); * and associated application properties.
init(applicationLayout); * @param applicationLayout application layout
} */
catch (IOException e) { public StandAloneApplication(ApplicationLayout applicationLayout) {
throw new AssertException(e); init(applicationLayout);
}
/**
* Read {@link ApplicationProperties} from the specified file path relative
* to the current working directory.
* <p>
* <b>The given properties file is expected to have the
* {@link ApplicationProperties#APPLICATION_NAME_PROPERTY} and
* {@link ApplicationProperties#APPLICATION_VERSION_PROPERTY} properties
* set.</b>
* @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) { private void init(ApplicationLayout applicationLayout) {

View file

@ -534,53 +534,7 @@ public class NamespaceUtils {
throws InvalidInputException { throws InvalidInputException {
Symbol namespaceSymbol = namespace.getSymbol(); Symbol namespaceSymbol = namespace.getSymbol();
String name = namespaceSymbol.getName();
SourceType originalSource = namespaceSymbol.getSource();
SymbolTable symbolTable = namespaceSymbol.getProgram().getSymbolTable(); SymbolTable symbolTable = namespaceSymbol.getProgram().getSymbolTable();
return symbolTable.convertNamespaceToClass(namespace);
// 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;
} }
} }

View file

@ -1695,34 +1695,41 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
@Override @Override
public void disassociate(DataType dataType) { 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. lock.acquire();
dataType.setSourceArchive(null); 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, // Set the source archive to null indicating no associated archive.
// since we no longer want the source archive data type's universal ID. dataType.setSourceArchive(null);
if (dataType instanceof DataTypeDB) {
DataTypeDB dt = (DataTypeDB) dataType; // Set the datatype's universal ID to a newly generated universal ID,
dt.setUniversalID(UniversalIdGenerator.nextID()); // 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);
} }
finally {
if (oldDtID != null) { lock.release();
idsToDataTypeMap.removeDataType(sourceArchive, oldDtID);
} }
dataTypeChanged(dataType);
} }
private Collection<DataType> filterOutNonSourceSettableDataTypes( private Collection<DataType> filterOutNonSourceSettableDataTypes(
@ -2694,9 +2701,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
} }
try { try {
creatingDataType++; creatingDataType++;
DBRecord record = functionDefAdapter.createRecord(name, funDef.getComment(), cat.getID(), DBRecord record =
DEFAULT_DATATYPE_ID, funDef.hasVarArgs(), funDef.getGenericCallingConvention(), functionDefAdapter.createRecord(name, funDef.getComment(), cat.getID(),
sourceArchiveIdValue, universalIdValue, funDef.getLastChangeTime()); DEFAULT_DATATYPE_ID, funDef.hasVarArgs(), funDef.getGenericCallingConvention(),
sourceArchiveIdValue, universalIdValue, funDef.getLastChangeTime());
FunctionDefinitionDB funDefDb = FunctionDefinitionDB funDefDb =
new FunctionDefinitionDB(this, dtCache, functionDefAdapter, paramAdapter, record); new FunctionDefinitionDB(this, dtCache, functionDefAdapter, paramAdapter, record);
@ -3696,7 +3704,13 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
@Override @Override
public DataType getDataType(SourceArchive sourceArchive, UniversalID datatypeID) { public DataType getDataType(SourceArchive sourceArchive, UniversalID datatypeID) {
UniversalID sourceID = sourceArchive == null ? null : sourceArchive.getSourceArchiveID(); UniversalID sourceID = sourceArchive == null ? null : sourceArchive.getSourceArchiveID();
return idsToDataTypeMap.getDataType(sourceID, datatypeID); lock.acquire();
try {
return idsToDataTypeMap.getDataType(sourceID, datatypeID);
}
finally {
lock.release();
}
} }
@Override @Override
@ -3829,7 +3843,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
monitor.setProgress(0); monitor.setProgress(0);
monitor.setMaximum(orderedComposites.size()); monitor.setMaximum(orderedComposites.size());
monitor.setMessage("Updating Datatype Sizes..."); monitor.setMessage("Updating Datatype Sizes...");
int count = 0; int count = 0;
for (CompositeDB c : orderedComposites) { for (CompositeDB c : orderedComposites) {
monitor.checkCanceled(); monitor.checkCanceled();
@ -4162,7 +4176,11 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
Map<UniversalID, DataType> idMap = Map<UniversalID, DataType> idMap =
map.computeIfAbsent(sourceID, k -> new ConcurrentHashMap<>()); 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, return idMap.computeIfAbsent(dataTypeID,
k -> findDataTypeForIDs(sourceArchiveID, dataTypeID)); k -> findDataTypeForIDs(sourceArchiveID, dataTypeID));
} }

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.address.AddressSetView;
import ghidra.program.model.listing.CircularDependencyException; import ghidra.program.model.listing.CircularDependencyException;
import ghidra.program.model.listing.GhidraClass; import ghidra.program.model.listing.GhidraClass;
import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.*;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
@ -63,6 +61,19 @@ class GhidraClassDB implements GhidraClass {
return symbol.getName(); 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() * @see ghidra.program.model.symbol.Namespace#getID()
*/ */

View file

@ -104,8 +104,9 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return false; return false;
} }
record = rec; record = rec;
address = symbolMgr.getAddressMap().decodeAddress( address = symbolMgr.getAddressMap()
rec.getLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL)); .decodeAddress(
rec.getLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL));
return true; return true;
} }
return false; return false;
@ -522,8 +523,8 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return source; return source;
} }
@Override public void doSetNameAndNamespace(String newName, Namespace newNamespace, SourceType source,
public void setNameAndNamespace(String newName, Namespace newNamespace, SourceType source) boolean checkForDuplicates)
throws DuplicateNameException, InvalidInputException, CircularDependencyException { throws DuplicateNameException, InvalidInputException, CircularDependencyException {
lock.acquire(); lock.acquire();
@ -563,7 +564,11 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
if (!namespaceChange && !nameChange) { if (!namespaceChange && !nameChange) {
return; return;
} }
symbolMgr.checkDuplicateSymbolName(address, newName, newNamespace, getSymbolType());
if (checkForDuplicates) {
symbolMgr.checkDuplicateSymbolName(address, newName, newNamespace,
getSymbolType());
}
} }
if (record != null) { 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<SymbolDB> getSymbolsDynamicallyRenamedByMyRename() { protected List<SymbolDB> getSymbolsDynamicallyRenamedByMyRename() {
return null; return null;
} }

View file

@ -40,7 +40,6 @@ import ghidra.program.util.LanguageTranslator;
import ghidra.util.*; import ghidra.util.*;
import ghidra.util.exception.*; import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
public class SymbolManager implements SymbolTable, ManagerDB { public class SymbolManager implements SymbolTable, ManagerDB {
@ -128,7 +127,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* Find previously defined variable storage address * Find previously defined variable storage address
* @param storage variable storage * @param storage variable storage
* @return previously defined variable storage address or null if not found * @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 { public Address findVariableStorageAddress(VariableStorage storage) throws IOException {
return variableStorageMgr.getVariableStorageAddress(storage, false); return variableStorageMgr.getVariableStorageAddress(storage, false);
@ -183,7 +182,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* where all the moved external addresses will be placed. * where all the moved external addresses will be placed.
* The triggering of this upgrade relies on the addition of the VariableManager which * The triggering of this upgrade relies on the addition of the VariableManager which
* trigger an upgrade. * trigger an upgrade.
* @param monitor * @param monitor the task monitor
*/ */
private boolean upgradeOldNamespaceAddresses(TaskMonitor monitor) private boolean upgradeOldNamespaceAddresses(TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
@ -239,9 +238,9 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Upgrade old stack and register variable symbol address to variable addresses. * Upgrade old stack and register variable symbol address to variable addresses.
* Also force associated references to be updated to new variable addresses. * Also force associated references to be updated to new variable addresses.
* @param monitor * @param monitor the task monitor
* @throws IOException * @throws IOException if there is database exception
* @throws CancelledException * @throws CancelledException if the operation is cancelled
*/ */
private void processOldVariableAddresses(TaskMonitor monitor) private void processOldVariableAddresses(TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
@ -296,8 +295,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* No more sharing the same variable address for multiple variable symbols. * No more sharing the same variable address for multiple variable symbols.
* Must split these up. Only reference to variable addresses should be the * Must split these up. Only reference to variable addresses should be the
* symbol address - reference refer to physical/stack addresses, and symbolIDs. * symbol address - reference refer to physical/stack addresses, and symbolIDs.
* @param monitor * @param monitor the task monitor
* @throws CancelledException * @throws CancelledException if the operation is cancelled
*/ */
public void migrateFromOldVariableStorageManager(TaskMonitor monitor) public void migrateFromOldVariableStorageManager(TaskMonitor monitor)
throws CancelledException { throws CancelledException {
@ -396,9 +395,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Add old local symbols * Add old local symbols
* @param table * @throws IOException if there is database exception
* @throws IOException * @throws CancelledException if the operation is cancelled
* @throws CancelledException
*/ */
private void processOldLocalSymbols(TaskMonitor monitor) private void processOldLocalSymbols(TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
@ -450,6 +448,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* @param oldAddr old address value from symbol table * @param oldAddr old address value from symbol table
* @param name symbol name * @param name symbol name
* @param isPrimary true if symbol is primary at oldAddr * @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, public static void saveLocalSymbol(DBHandle tmpHandle, long symbolID, long oldAddr, String name,
boolean isPrimary) throws IOException { boolean isPrimary) throws IOException {
@ -480,18 +479,15 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return; return;
} }
List<Symbol> symbolList = getSymbols(name, namespace); Symbol symbol = getFirstSymbol(name, namespace, s -> !s.getSymbolType().allowsDuplicates());
for (Symbol symbol : symbolList) { if (symbol != null) {
if (!symbol.getSymbolType().allowsDuplicates()) { throw new DuplicateNameException(
throw new DuplicateNameException( "A " + symbol.getSymbolType() + " symbol with name " + name +
"A " + symbol.getSymbolType() + " symbol with name " + name + " already exists in namespace " + symbol.getParentNamespace().getName());
" already exists in namespace " + symbol.getParentNamespace().getName());
}
} }
} }
/** /*
* Convert the specified dynamic symbol to a named symbol. Both symbol removed and symbol added * 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 * notifications are performed, although the symbol instance is changed and continues to be
* valid. * valid.
@ -509,8 +505,9 @@ public class SymbolManager implements SymbolTable, ManagerDB {
Address address = symbol.getAddress(); Address address = symbol.getAddress();
symbolRemoved(symbol, address, symbol.getName(), oldKey, Namespace.GLOBAL_NAMESPACE_ID, symbolRemoved(symbol, address, symbol.getName(), oldKey, Namespace.GLOBAL_NAMESPACE_ID,
null); null);
DBRecord record = adapter.createSymbol(newName, address, newParentID, SymbolType.LABEL, 0, DBRecord record =
1, null, source); adapter.createSymbol(newName, address, newParentID, SymbolType.LABEL, 0,
1, null, source);
symbol.setRecord(record);// symbol object was morphed symbol.setRecord(record);// symbol object was morphed
symbolAdded(symbol); symbolAdded(symbol);
} }
@ -987,10 +984,11 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return searchSymbolsByNamespaceFirst(name, namespace); return searchSymbolsByNamespaceFirst(name, namespace);
} }
// Try to find the symbols by searching through all the symbols with the given name and including // Try to find the symbols by searching through all the symbols with the given name
// only those in the specified namespace. If there are too many symbols with the same name and // and including only those in the specified namespace. If there are too many symbols
// we are not in the global space, abandon this approach and instead search through all // with the same name and we are not in the global space, abandon this approach and
// the symbols in the namespace and only include those with the specified name. // instead search through all the symbols in the namespace and only include those with
// the specified name.
int count = 0; int count = 0;
List<Symbol> list = new ArrayList<>(); List<Symbol> list = new ArrayList<>();
SymbolIterator symbols = getSymbols(name); 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<Symbol> 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. * 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 * Returns the next available external symbol address
* @return * @return the address
*/ */
public Address getNextExternalSymbolAddress() { public Address getNextExternalSymbolAddress() {
int extID = 1; int extID = 1;
@ -1414,6 +1448,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
if (!symbol.isDynamic()) { if (!symbol.isDynamic()) {
createLabelHistoryRecord(addr, oldName, newName, LabelHistory.RENAME); createLabelHistoryRecord(addr, oldName, newName, LabelHistory.RENAME);
} }
program.symbolChanged(symbol, ChangeManager.DOCR_SYMBOL_RENAMED, addr, symbol, oldName, program.symbolChanged(symbol, ChangeManager.DOCR_SYMBOL_RENAMED, addr, symbol, oldName,
newName); newName);
} }
@ -1493,7 +1528,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
// fire event // fire event
program.symbolChanged(symbol, ChangeManager.DOCR_SYMBOL_REMOVED, addr, symbol, name, program.symbolChanged(symbol, ChangeManager.DOCR_SYMBOL_REMOVED, addr, symbol, name,
new Long(symbolID)); symbolID);
} }
void externalEntryPointRemoved(Address addr) { void externalEntryPointRemoved(Address addr) {
@ -1543,11 +1578,6 @@ public class SymbolManager implements SymbolTable, ManagerDB {
Symbol sym; Symbol sym;
/**
* Construct iterator which returns a single symbol
*
* @param addr
*/
SingleSymbolIterator(Symbol sym) { SingleSymbolIterator(Symbol sym) {
this.sym = sym; this.sym = sym;
} }
@ -2175,8 +2205,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
void moveLabelHistory(Address oldAddress, Address address) { void moveLabelHistory(Address oldAddress, Address address) {
try { try {
historyAdapter.moveAddressRange(oldAddress, address, 1, addrMap, historyAdapter.moveAddressRange(oldAddress, address, 1, addrMap, TaskMonitor.DUMMY);
TaskMonitorAdapter.DUMMY_MONITOR);
} }
catch (CancelledException e) { catch (CancelledException e) {
// can't happen, used dummy monitor // can't happen, used dummy monitor
@ -2266,6 +2295,109 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return new NamespaceDB(s, namespaceMgr); 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 @Override
public Symbol createSymbolPlaceholder(Address address, long id) { public Symbol createSymbolPlaceholder(Address address, long id) {
return SymbolDB.createSymbolPlaceholder(this, address, 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) SymbolType symbolType, long data1, int data2, String data3, SourceType source)
throws DuplicateNameException, InvalidInputException { 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(); lock.acquire();
try { try {
parent = validateNamespace(parent, addr, symbolType); parent = validateNamespace(parent, addr, symbolType);
source = validateSource(source, name, addr, symbolType); source = validateSource(source, name, addr, symbolType);
name = validateName(name, source); 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); 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 addr the address for the new symbol
* @param name the name of the new symbol * @param name the name of the new symbol
* @param namespace the namespace for 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) { long data1, int data2, String data3, SourceType source) {
try { try {
DBRecord record = adapter.createSymbol(name, addr, namespace.getID(), type, data1, data2, DBRecord record =
data3, source); adapter.createSymbol(name, addr, namespace.getID(), type, data1, data2,
data3, source);
SymbolDB newSymbol = makeSymbol(addr, record, type); SymbolDB newSymbol = makeSymbol(addr, record, type);
symbolAdded(newSymbol); symbolAdded(newSymbol);
@ -2633,19 +2779,29 @@ public class SymbolManager implements SymbolTable, ManagerDB {
@Override @Override
public Symbol getVariableSymbol(String name, Function function) { public Symbol getVariableSymbol(String name, Function function) {
return findFirstSymbol(name, function, s -> { return getFirstSymbol(name, function, s -> {
SymbolType t = s.getSymbolType(); SymbolType t = s.getSymbolType();
return t == SymbolType.PARAMETER || t == SymbolType.LOCAL_VAR; return t == SymbolType.PARAMETER || t == SymbolType.LOCAL_VAR;
}); });
} }
private Symbol getSpecificSymbol(String name, Namespace namespace, SymbolType type) { 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<Symbol> test) { private Symbol findFirstSymbol(String name, Namespace namespace, Predicate<Symbol> test) {
List<Symbol> symbols = getSymbols(name, namespace); if (namespace == null) {
return symbols.stream().filter(test).findFirst().orElse(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;
} }
} }

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.program.model.symbol; package ghidra.program.model.symbol;
import java.util.Iterator; import java.util.*;
import java.util.List;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
@ -504,7 +503,7 @@ public interface SymbolTable {
* @param name name of the namespace * @param name name of the namespace
* @param source the source of this class namespace's symbol * @param source the source of this class namespace's symbol
* @return new class namespace * @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 InvalidInputException throw if the name has invalid characters or is null
* @throws IllegalArgumentException if you try to set the source to 'Symbol.DEFAULT'. * @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 * @param source the source of this external library's symbol
* @return the new Library namespace. * @return the new Library namespace.
* @throws IllegalArgumentException if you try to set the source to 'Symbol.DEFAULT'. * @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) public Library createExternalLibrary(String name, SourceType source)
throws DuplicateNameException, InvalidInputException; throws DuplicateNameException, InvalidInputException;
@ -534,7 +533,7 @@ public interface SymbolTable {
* @param name the name of the new namespace * @param name the name of the new namespace
* @param source the source of this namespace's symbol * @param source the source of this namespace's symbol
* @return the new Namespace object. * @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 InvalidInputException if the name is invalid.
* @throws IllegalArgumentException if you try to set the source to 'Symbol.DEFAULT'. * @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); 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;
} }

View file

@ -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; } 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 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 fpscr_t: "FPSCR" is OP_0 { x:4 = 0; export x; } # dummy export, just looking for the display

View file

@ -45,8 +45,8 @@ fi
OWNER="$(grep '^wrapper.app.account=' "${CONFIG}" | sed -e 's/^.*=\(.*\)\s*.*$/\1/')" OWNER="$(grep '^wrapper.app.account=' "${CONFIG}" | sed -e 's/^.*=\(.*\)\s*.*$/\1/')"
if [ -z "${OWNER}" -o "${OWNER}" = "$(whoami)" ]; then if [ -z "${OWNER}" -o "${OWNER}" = "$(whoami)" ]; then
VMARGS="-DUserAdmin.invocation=$(basename "${SCRIPT_FILE}") -DUserAdmin.config=\"${CONFIG}\"" VMARGS="-DUserAdmin.invocation=$(basename "${SCRIPT_FILE}")"
"${SCRIPT_DIR}"/../support/launch.sh fg svrAdmin "${MAXMEM}" "$VMARGS" ghidra.server.ServerAdmin "$@" "${SCRIPT_DIR}"/../support/launch.sh fg svrAdmin "${MAXMEM}" "$VMARGS" ghidra.server.ServerAdmin "${CONFIG}" "$@"
else else
echo "Running svrAdmin with $SUDO as ${OWNER} ..." echo "Running svrAdmin with $SUDO as ${OWNER} ..."
$SUDO -u $OWNER "$0" "$@" $SUDO -u $OWNER "$0" "$@"

View file

@ -25,7 +25,7 @@ if [ ! -d "${GHIDRA_ROOT_DIR}" ]; then
fi fi
# Set required VMARGS for jar builder application # 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 # Launch jar builder
"${SCRIPT_DIR}"/launch.sh "${LAUNCH_MODE}" Ghidra "${MAXMEM}" "${APP_VMARGS}" ghidra.util.GhidraJarBuilder -main ghidra.JarRun "$@" "${SCRIPT_DIR}"/launch.sh "${LAUNCH_MODE}" Ghidra "${MAXMEM}" "${APP_VMARGS}" ghidra.util.GhidraJarBuilder -main ghidra.JarRun "$@"

View file

@ -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 it may be necessary to explicitly specify the path to the installation by setting JAVA_HOME
rem below: rem below:
rem set JAVA_HOME= rem set "JAVA_HOME="
setlocal enabledelayedexpansion setlocal enabledelayedexpansion
@ -50,7 +50,7 @@ if "%IS_ADMIN%"=="NO" (
rem Find the script directory rem Find the script directory
rem %~dsp0 is location of current script under NT rem %~dsp0 is location of current script under NT
set _REALPATH=%~dp0 set "_REALPATH=%~dp0"
set APP_NAME=ghidraSvr set APP_NAME=ghidraSvr
set APP_LONG_NAME=Ghidra Server 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 NOTE: If adjusting JAVA command assignment - do not attempt to add parameters (e.g., -d64, -version:1.7, etc.)
rem Development Environment rem Development Environment
set GHIDRA_HOME=%_REALPATH%..\..\..\.. set "GHIDRA_HOME=%_REALPATH%..\..\..\.."
set WRAPPER_CONF=%_REALPATH%..\..\Common\server\server.conf set "WRAPPER_CONF=%_REALPATH%..\..\Common\server\server.conf"
set DATA_DIR=%GHIDRA_HOME%\%MODULE_DIR%\build\data set "DATA_DIR=%GHIDRA_HOME%\%MODULE_DIR%\build\data"
set CLASSPATH_FRAG=%GHIDRA_HOME%\%MODULE_DIR%\build\dev-meta\classpath.frag set "CLASSPATH_FRAG=%GHIDRA_HOME%\%MODULE_DIR%\build\dev-meta\classpath.frag"
set LS_CPATH=%GHIDRA_HOME%\GhidraBuild\LaunchSupport\bin\main set "LS_CPATH=%GHIDRA_HOME%\GhidraBuild\LaunchSupport\bin\main"
goto lab1 goto lab1
:normal :normal
set GHIDRA_HOME=%_REALPATH%.. set "GHIDRA_HOME=%_REALPATH%.."
set WRAPPER_CONF=%_REALPATH%server.conf set "WRAPPER_CONF=%_REALPATH%server.conf"
set DATA_DIR=%GHIDRA_HOME%\%MODULE_DIR%\data set "DATA_DIR=%GHIDRA_HOME%\%MODULE_DIR%\data"
set CLASSPATH_FRAG=%GHIDRA_HOME%\%MODULE_DIR%\data\classpath.frag set "CLASSPATH_FRAG=%GHIDRA_HOME%\%MODULE_DIR%\data\classpath.frag"
set LS_CPATH=%GHIDRA_HOME%\support\LaunchSupport.jar set "LS_CPATH=%GHIDRA_HOME%\support\LaunchSupport.jar"
:lab1 :lab1
rem set WRAPPER_HOME to unpacked yajsw location (crazy FOR syntax to set variable from command output) 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 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%\" ( if not exist "%WRAPPER_HOME%\" (
echo. echo.
@ -104,8 +104,7 @@ set ERROR=ERROR: JAVA_HOME is not set and no 'java' command could be found in yo
goto reportError goto reportError
:findJavaFromJavaHome :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 if exist "%JAVA%" goto lab2
set ERROR=ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 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 rem reestablish JAVA path based upon final JAVA_HOME
set JAVA=%JAVA_HOME%\bin\java.exe 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 DEBUG=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=*:18888 :: set DEBUG=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=*:18888

View file

@ -31,18 +31,18 @@ set MAXMEM=128M
:: '% ~' dereferences the value in param 0 :: '% ~' dereferences the value in param 0
:: 'd' - drive :: 'd' - drive
:: 'p' - path (without filename) :: 'p' - path (without filename)
set SCRIPT_DIR=%~dp0 set "SCRIPT_DIR=%~dp0"
:: Production Environment :: Production Environment
set CONFIG=%SCRIPT_DIR%.\server.conf set "CONFIG=%SCRIPT_DIR%.\server.conf"
if exist "%CONFIG%" goto continue if exist "%CONFIG%" goto continue
:: Development Environment :: Development Environment
set CONFIG=%SCRIPT_DIR%..\..\Common\server\server.conf set "CONFIG=%SCRIPT_DIR%..\..\Common\server\server.conf"
:continue :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%" %*

View file

@ -3,7 +3,7 @@ setlocal
rem Find the script directory rem Find the script directory
rem %~dsp0 is location of current script under NT rem %~dsp0 is location of current script under NT
set _REALPATH=%~dp0 set "_REALPATH=%~dp0"
call "%_REALPATH%\ghidraSvr" install call "%_REALPATH%\ghidraSvr" install

View file

@ -3,7 +3,7 @@ setlocal
rem Find the script directory rem Find the script directory
rem %~dsp0 is location of current script under NT rem %~dsp0 is location of current script under NT
set _REALPATH=%~dp0 set "_REALPATH=%~dp0"
call "%_REALPATH%\ghidraSvr" uninstall call "%_REALPATH%\ghidraSvr" uninstall

View file

@ -24,7 +24,7 @@ set VMARG_LIST=-XX:ParallelGCThreads=2
set VMARG_LIST=%VMARG_LIST% -XX:CICompilerCount=2 set VMARG_LIST=%VMARG_LIST% -XX:CICompilerCount=2
:: store current path :: store current path
set filepath=%~dp0 set "filepath=%~dp0"
:: Loop through parameters (if there aren't any, just continue) and store :: Loop through parameters (if there aren't any, just continue) and store
:: in params variable. :: in params variable.

View file

@ -8,9 +8,9 @@ setlocal
set LAUNCH_MODE=fg set LAUNCH_MODE=fg
:: Sets SCRIPT_DIR to the directory that contains this file (ends with '\') :: 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 if exist "%GHIDRA_ROOT_DIR%" goto continue
echo This script does not support development mode use echo This script does not support development mode use
@ -18,6 +18,6 @@ exit /B 1
:continue :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 %* call "%~dp0launch.bat" %LAUNCH_MODE% Ghidra "" "%APP_VMARGS%" ghidra.util.GhidraJarBuilder -main ghidra.JarRun %*

View file

@ -11,16 +11,16 @@
setlocal setlocal
REM Get parent of current folder 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 set OS_DIR=os
REM Production Environment REM Production Environment
if exist "%GHIDRA_DIR%" goto continue if exist "%GHIDRA_DIR%" goto continue
REM Development Environment REM Development Environment
set GHIDRA_DIR=%SCRIPT_DIR%..\..\.. set "GHIDRA_DIR=%SCRIPT_DIR%..\..\.."
set OS_DIR=build\os set OS_DIR=build\os
:continue :continue
@ -35,7 +35,7 @@ if exist "%PROGRAMFILES(X86)%" (
set OS_TYPE=win32 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%" ( if not exist "%PDB_EXE%" (
echo "%PDB_EXE% not found" echo "%PDB_EXE% not found"

View file

@ -36,7 +36,7 @@ for /f "tokens=2" %%# in ("%cmdcmdline%") do if /i "%%#" equ "/c" set DOUBLE_CLI
:: '% ~' dereferences the value in param 0 :: '% ~' dereferences the value in param 0
:: 'd' - drive :: 'd' - drive
:: 'p' - path (without filename) :: 'p' - path (without filename)
set SUPPORT_DIR=%~dp0 set "SUPPORT_DIR=%~dp0"
:: ::
:: Parse arguments :: Parse arguments
@ -63,20 +63,20 @@ goto showUsage
:: ::
:: Production Environment :: Production Environment
:: ::
set INSTALL_DIR=%SUPPORT_DIR%..\ set "INSTALL_DIR=%SUPPORT_DIR%..\"
set CPATH=%INSTALL_DIR%Ghidra\Framework\Utility\lib\Utility.jar set "CPATH=%INSTALL_DIR%Ghidra\Framework\Utility\lib\Utility.jar"
set LS_CPATH=%SUPPORT_DIR%LaunchSupport.jar set "LS_CPATH=%SUPPORT_DIR%LaunchSupport.jar"
set DEBUG_LOG4J=%SUPPORT_DIR%debug.log4j.xml set "DEBUG_LOG4J=%SUPPORT_DIR%debug.log4j.xml"
if exist "%INSTALL_DIR%Ghidra" goto continue2 if exist "%INSTALL_DIR%Ghidra" goto continue2
:: ::
:: Development Environment :: Development Environment
:: ::
set INSTALL_DIR=%INSTALL_DIR%..\..\..\ set "INSTALL_DIR=%INSTALL_DIR%..\..\..\"
set CPATH=%INSTALL_DIR%Ghidra\Framework\Utility\bin\main set "CPATH=%INSTALL_DIR%Ghidra\Framework\Utility\bin\main"
set LS_CPATH=%INSTALL_DIR%GhidraBuild\LaunchSupport\bin\main set "LS_CPATH=%INSTALL_DIR%GhidraBuild\LaunchSupport\bin\main"
set DEBUG_LOG4J=%INSTALL_DIR%Ghidra\RuntimeScripts\Common\support\debug.log4j.xml set "DEBUG_LOG4J=%INSTALL_DIR%Ghidra\RuntimeScripts\Common\support\debug.log4j.xml"
:continue2 :continue2
@ -108,7 +108,7 @@ if "%JAVA_HOME%" == "" (
goto exit1 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 :: 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 for /f "delims=*" %%i in ('java -cp "%LS_CPATH%" LaunchSupport "%INSTALL_DIR%\" -vmargs') do set VMARG_LIST=%VMARG_LIST% %%i

View file

@ -26,6 +26,6 @@ set VMARG_LIST=-XX:ParallelGCThreads=2
set VMARG_LIST=%VMARG_LIST% -XX:CICompilerCount=2 set VMARG_LIST=%VMARG_LIST% -XX:CICompilerCount=2
:: store current path :: store current path
set filepath=%~dp0 set "filepath=%~dp0"
call "%filepath%launch.bat" %LAUNCH_MODE% Ghidra-Python "%MAXMEM%" "%VMARG_LIST%" ghidra.python.PythonRun %params% call "%filepath%launch.bat" %LAUNCH_MODE% Ghidra-Python "%MAXMEM%" "%VMARG_LIST%" ghidra.python.PythonRun %params%